/********************************************************************++
Copyright (c) Microsoft Corporation.  All rights reserved.
--********************************************************************/


// Implementation notes: In the functions that take ConsoleHandle parameters, we only assert that the handle is valid and not
// closed, as opposed to doing a check and throwing an exception.  This is because the win32 APIs that those functions wrap will 
// fail on invalid/closed handles, and the check for API failure will throw the exception.
//
// On the use of DangerousGetHandle: If the handle has been invalidated, then the API we pass it to will return an error.  These 
// handles should not be exposed to recycling attacks (because they are not exposed at all), but if they were, the worse they 
// could do is diddle with the console buffer.
#pragma warning disable 1634, 1691


using System;
using System.Text;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Management.Automation;
using System.Management.Automation.Host;
using System.ComponentModel;
using System.Collections.Generic;
using System.Globalization;
using System.Diagnostics;
using Microsoft.Win32.SafeHandles;

using ConsoleHandle = Microsoft.Win32.SafeHandles.SafeFileHandle;

using WORD   = System.UInt16;
using ULONG  = System.UInt32;
using DWORD  = System.UInt32;
using NakedWin32Handle = System.IntPtr;
using HWND = System.IntPtr;
using HDC = System.IntPtr;

using MS.Dbg;

namespace MS.DbgShell
{
    /// <summary>
    ///
    /// Class ConsoleControl is used to wrap the various win32 console APIs 1:1 (i.e. at a low level, without attempting to be a 
    /// "true" object-oriented library.
    ///
    /// </summary>

    internal static partial class ConsoleControl
    {
#region structs

        internal enum InputRecordEventTypes : ushort
        {
            // from wincon.h.  These look like bit flags, but of course they could not really be used that way, since it would 
            // not make sense to have more than one of the INPUT_RECORD union members "in effect" at any one time.

            KEY_EVENT = 0x0001,
            MOUSE_EVENT = 0x0002,
            WINDOW_BUFFER_SIZE_EVENT = 0x0004,
            MENU_EVENT = 0x0008,
            FOCUS_EVENT = 0x0010
        }

        [StructLayout(LayoutKind.Sequential)]
        internal struct INPUT_RECORD
        {
            internal WORD EventType;
            internal KEY_EVENT_RECORD KeyEvent;
        }

        [Flags]
        internal enum ControlKeyStates : uint
        {
            // From wincon.h.
            RIGHT_ALT_PRESSED = 0x0001, // the right alt key is pressed.
            LEFT_ALT_PRESSED = 0x0002, // the left alt key is pressed.
            RIGHT_CTRL_PRESSED = 0x0004, // the right ctrl key is pressed.
            LEFT_CTRL_PRESSED = 0x0008, // the left ctrl key is pressed.
            SHIFT_PRESSED = 0x0010, // the shift key is pressed.
            NUMLOCK_ON = 0x0020, // the numlock light is on.
            SCROLLLOCK_ON = 0x0040, // the scrolllock light is on.
            CAPSLOCK_ON = 0x0080, // the capslock light is on.
            ENHANCED_KEY = 0x0100  // the key is enhanced.
        }

        // LayoutKind must be Explicit
        [StructLayout(LayoutKind.Sequential)]
        internal struct KEY_EVENT_RECORD
        {
            internal bool KeyDown;

            internal WORD RepeatCount;

            internal WORD VirtualKeyCode;

            internal WORD VirtualScanCode;

            internal char UnicodeChar;

            internal DWORD ControlKeyState;
        }

        [StructLayout(LayoutKind.Sequential)]
        internal struct COORD
        {
            internal short X;

            internal short Y;

            public override string ToString()
            {
                return string.Format(CultureInfo.InvariantCulture, "{0},{1}", X, Y);
            }
        }

        [StructLayout(LayoutKind.Sequential)]
        internal struct CONSOLE_READCONSOLE_CONTROL
        {
            // from public/internal/windows/inc/winconp.h
            internal ULONG nLength;

            internal ULONG nInitialChars;

            internal ULONG dwCtrlWakeupMask;

            internal /* out */ ULONG dwControlKeyState;
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        internal struct CONSOLE_FONT_INFO_EX
        {
            internal int cbSize;
            internal int nFont;
            internal short FontWidth;
            internal short FontHeight;
            internal int FontFamily;
            internal int FontWeight;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
            internal string FontFace;
        }

        [StructLayout(LayoutKind.Sequential)]
        internal struct CHAR_INFO
        {
            internal ushort UnicodeChar;

            internal WORD Attributes;
        }

        [StructLayout(LayoutKind.Sequential)]
        internal struct SMALL_RECT
        {
            internal short Left;

            internal short Top;

            internal short Right;

            internal short Bottom;

            public override string ToString()
            {
                return String.Format(CultureInfo.InvariantCulture, "{0},{1},{2},{3}", Left, Top, Right, Bottom);
            }
        }

        [StructLayout(LayoutKind.Sequential)]
        internal struct CONSOLE_SCREEN_BUFFER_INFO
        {
            internal COORD BufferSize;

            internal COORD CursorPosition;

            internal WORD Attributes;

            internal SMALL_RECT WindowRect;

            internal COORD MaxWindowSize;

            // NTRAID#Windows Out Of Band Releases-938428-2006/07/17-jwh
            // Bring the total size of the struct to 24 bytes.
            internal DWORD Padding;
        }

        [StructLayout(LayoutKind.Sequential)]
        internal struct CONSOLE_CURSOR_INFO
        {
            internal DWORD Size;

            internal bool Visible;

            public override string ToString()
            {
                return String.Format(CultureInfo.InvariantCulture, "Size: {0}, Visible: {1}", Size, Visible);
            }
        }

        [StructLayout(LayoutKind.Sequential)]
        internal struct FONTSIGNATURE
        {
            //From public\sdk\inc\wingdi.h

            // fsUsb*: A 128-bit Unicode subset bitfield (USB) identifying up to 126 Unicode subranges
            internal DWORD fsUsb0;
            internal DWORD fsUsb1;
            internal DWORD fsUsb2;
            internal DWORD fsUsb3;
            // fsCsb*: A 64-bit, code-page bitfield (CPB) that identifies a specific character set or code page.
            internal DWORD fsCsb0;
            internal DWORD fsCsb1;
        }

        [StructLayout(LayoutKind.Sequential)]
        internal struct CHARSETINFO
        {
            //From public\sdk\inc\wingdi.h
            internal uint ciCharset;   // Character set value.
            internal uint ciACP;       // ANSI code-page identifier.
            internal FONTSIGNATURE fs;
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        internal struct TEXTMETRIC
        {
            //From public\sdk\inc\wingdi.h
            public int tmHeight;
            public int tmAscent;
            public int tmDescent;
            public int tmInternalLeading;
            public int tmExternalLeading;
            public int tmAveCharWidth;
            public int tmMaxCharWidth;
            public int tmWeight;
            public int tmOverhang;
            public int tmDigitizedAspectX;
            public int tmDigitizedAspectY;
            public char tmFirstChar;
            public char tmLastChar;
            public char tmDefaultChar;
            public char tmBreakChar;
            public byte tmItalic;
            public byte tmUnderlined;
            public byte tmStruckOut;
            public byte tmPitchAndFamily;
            public byte tmCharSet;
        }

#region SentInput Data Structures

        [StructLayout(LayoutKind.Sequential)]
        internal struct INPUT
        {
            internal DWORD Type;
            internal MouseKeyboardHardwareInput Data;
        }

        [StructLayout(LayoutKind.Explicit)]
        internal struct MouseKeyboardHardwareInput
        {
            [FieldOffset(0)]
            internal MouseInput Mouse;

            [FieldOffset(0)]
            internal KeyboardInput Keyboard;

            [FieldOffset(0)]
            internal HardwareInput Hardware;
        }

        [StructLayout(LayoutKind.Sequential)]
        internal struct MouseInput
        {
            /// <summary>
            /// The absolute position of the mouse, or the amount of motion since the last mouse event was generated, depending on the value of the dwFlags member. 
            /// Absolute data is specified as the x coordinate of the mouse; relative data is specified as the number of pixels moved.
            /// </summary>
            internal Int32 X;

            /// <summary>
            /// The absolute position of the mouse, or the amount of motion since the last mouse event was generated, depending on the value of the dwFlags member. 
            /// Absolute data is specified as the y coordinate of the mouse; relative data is specified as the number of pixels moved.
            /// </summary>
            internal Int32 Y;

            /// <summary>
            /// If dwFlags contains MOUSEEVENTF_WHEEL, then mouseData specifies the amount of wheel movement. A positive value indicates that the wheel was rotated forward, away from the user; 
            /// a negative value indicates that the wheel was rotated backward, toward the user. One wheel click is defined as WHEEL_DELTA, which is 120.
            /// </summary>
            internal DWORD MouseData;

            /// <summary>
            /// A set of bit flags that specify various aspects of mouse motion and button clicks.
            /// See (http://msdn.microsoft.com/en-us/library/ms646273(VS.85).aspx)
            /// </summary>
            internal DWORD Flags;

            /// <summary>
            /// The time stamp for the event, in milliseconds. If this parameter is 0, the system will provide its own time stamp. 
            /// </summary>
            internal DWORD Time;

            /// <summary>
            /// An additional value associated with the mouse event. An application calls GetMessageExtraInfo to obtain this extra information
            /// </summary>
            internal IntPtr ExtraInfo;
        }

        [StructLayout(LayoutKind.Sequential)]
        internal struct KeyboardInput
        {
            /// <summary>
            /// A virtual-key code. The code must be a value in the range 1 to 254.
            /// If the dwFlags member specifies KEYEVENTF_UNICODE, wVk must be 0.
            /// </summary>
            internal WORD Vk;

            /// <summary>
            /// A hardware scan code for the key. If dwFlags specifies KEYEVENTF_UNICODE, 
            /// wScan specifies a Unicode character which is to be sent to the foreground application.
            /// </summary>
            internal WORD Scan;

            /// <summary>
            /// Specifies various aspects of a keystroke.
            /// This member can be certain combinations of the following values.
            /// </summary>
            internal DWORD Flags;

            /// <summary>
            /// The time stamp for the event, in milliseconds.
            /// If this parameter is zero, the system will provide its own time stamp.
            /// </summary>
            internal DWORD Time;

            /// <summary>
            /// An additional value associated with the keystroke. 
            /// Use the GetMessageExtraInfo function to obtain this information. 
            /// </summary>
            internal IntPtr ExtraInfo;
        }

        [StructLayout(LayoutKind.Sequential)]
        internal struct HardwareInput
        {
            /// <summary>
            /// The message generated by the input hardware.
            /// </summary>
            internal DWORD Msg;

            /// <summary>
            /// The low-order word of the lParam parameter for uMsg.
            /// </summary>
            internal WORD ParamL;

            /// <summary>
            /// The high-order word of the lParam parameter for uMsg.
            /// </summary>
            internal WORD ParamH;
        }

        internal enum VirtualKeyCode : ushort
        {
            /// <summary>
            /// LEFT ARROW key
            /// </summary>
            Left = 0x25,

            /// <summary>
            /// ENTER key
            /// </summary>
            Return = 0x0D,
        }

        /// <summary>
        /// Specify the type of the input
        /// </summary>
        internal enum InputType : uint
        {
            /// <summary>
            /// INPUT_MOUSE = 0x00
            /// </summary>
            Mouse = 0,

            /// <summary>
            /// INPUT_KEYBOARD = 0x01
            /// </summary>
            Keyboard = 1,

            /// <summary>
            /// INPUT_HARDWARE = 0x02
            /// </summary>
            Hardware = 2,
        }

        internal enum KeyboardFlag : uint
        {
            /// <summary>
            /// If specified, the scan code was preceded by a prefix byte that has the value 0xE0 (224).
            /// </summary>
            ExtendedKey = 0x0001,

            /// <summary>
            /// If specified, the key is being released. If not specified, the key is being pressed.
            /// </summary>
            KeyUp = 0x0002,

            /// <summary>
            /// If specified, wScan identifies the key and wVk is ignored. 
            /// </summary>
            Unicode = 0x0004,

            /// <summary>
            /// If specified, the system synthesizes a VK_PACKET keystroke. The wVk parameter must be zero. 
            /// This flag can only be combined with the KEYEVENTF_KEYUP flag.
            /// </summary>
            ScanCode = 0x0008
        }

#endregion SentInput Data Structures

#endregion structs

#region Window Visibility
        [DllImport("Kernel32.dll")]
        internal static extern IntPtr GetConsoleWindow();

        internal const int SW_HIDE = 0;
        internal const int SW_SHOWNORMAL = 1;
        internal const int SW_NORMAL = 1;
        internal const int SW_SHOWMINIMIZED = 2;
        internal const int SW_SHOWMAXIMIZED = 3;
        internal const int SW_MAXIMIZE = 3;
        internal const int SW_SHOWNOACTIVATE = 4;
        internal const int SW_SHOW = 5;
        internal const int SW_MINIMIZE = 6;
        internal const int SW_SHOWMINNOACTIVE = 7;
        internal const int SW_SHOWNA = 8;
        internal const int SW_RESTORE = 9;
        internal const int SW_SHOWDEFAULT = 10;
        internal const int SW_FORCEMINIMIZE = 11;
        internal const int SW_MAX = 11;

        /// <summary>
        /// Code to control the display properties of the a window...
        /// </summary>
        /// <param name="hWnd">The window to show...</param>
        /// <param name="nCmdShow">The command to do</param>
        /// <returns>true it it was successful</returns>
        [DllImport("user32.dll")]
        internal static extern bool ShowWindow(IntPtr hWnd, Int32 nCmdShow);

        internal static void SetConsoleMode(ProcessWindowStyle style)
        {
            IntPtr hwnd = GetConsoleWindow();
            Util.Assert(hwnd != IntPtr.Zero, "Console handle should never be zero");
            switch (style)
            {
                case ProcessWindowStyle.Hidden:
                    ShowWindow(hwnd, SW_HIDE);
                    break;
                case ProcessWindowStyle.Maximized:
                    ShowWindow(hwnd, SW_MAXIMIZE);
                    break;
                case ProcessWindowStyle.Minimized:
                    ShowWindow(hwnd, SW_MINIMIZE);
                    break;
                case ProcessWindowStyle.Normal:
                    ShowWindow(hwnd, SW_NORMAL);
                    break;
            }
        }
#endregion

#region Input break handler (Ctrl-C, Ctrl-Break)

        /// <summary>
        /// 
        /// Types of control ConsoleBreakSignals received by break Win32Handler delegates
        /// 
        /// </summary>

        internal enum ConsoleBreakSignal : uint
        {
            // These correspond to the CRTL_XXX_EVENT #defines in public/sdk/inc/wincon.h

            CtrlC = 0,
            CtrlBreak = 1,
            Close = 2,
            Logoff = 5,

            // This only gets received by services

            Shutdown = 6,

            // None is not really a signal -- it's used to indicate that no signal exists.

            None = 0xFF
        }

        // NOTE: this delegate will be executed in its own thread 

        internal delegate bool BreakHandler(ConsoleBreakSignal ConsoleBreakSignal);

        /// <summary>
        /// Set the console's break handler
        /// </summary>
        /// <param name="handlerDelegate"></param>
        /// <exception cref="HostException">
        /// If Win32's SetConsoleCtrlHandler fails
        /// </exception>

        internal static void AddBreakHandler(BreakHandler handlerDelegate)
        {
            bool result = NativeMethods.SetConsoleCtrlHandler(handlerDelegate, true);

            if (result == false)
            {
                int err = Marshal.GetLastWin32Error();

                HostException e = CreateHostException(err, "AddBreakHandler",
                    ErrorCategory.ResourceUnavailable, "The Win32 internal error \"{0}\" 0x{1:X} occurred when adding a break handler. Contact Microsoft Support Services."); //ConsoleControlStrings.AddBreakHandlerExceptionMessage);
                throw e;
            }
        }

        /// <summary>
        /// Set the console's break handler to null
        /// </summary>
        /// <exception cref="HostException">
        /// If Win32's SetConsoleCtrlHandler fails
        /// </exception>

        internal static void RemoveBreakHandler()
        {
            bool result = NativeMethods.SetConsoleCtrlHandler(null, false);

            if (result == false)
            {
                int err = Marshal.GetLastWin32Error();

                HostException e = CreateHostException(err, "RemoveBreakHandler",
                    ErrorCategory.ResourceUnavailable, "The Win32 internal error \"{0}\" 0x{1:X} occurred while trying to remove a break handler. Contact Microsoft Customer Support Services." ); //ConsoleControlStrings.RemoveBreakHandlerExceptionTemplate);
                throw e;
            }
        }

#endregion

#region Win32Handles

        static readonly Lazy<ConsoleHandle> _inputHandle = new Lazy<SafeFileHandle>(() =>
            {
                var handle = NativeMethods.CreateFile(
                    "CONIN$",
                    (UInt32)
                    (NativeMethods.AccessQualifiers.GenericRead | NativeMethods.AccessQualifiers.GenericWrite),
                    (UInt32)NativeMethods.ShareModes.ShareRead,
                    (IntPtr)0,
                    (UInt32)NativeMethods.CreationDisposition.OpenExisting,
                    0,
                    (IntPtr)0);

                if (handle == NativeMethods.INVALID_HANDLE_VALUE)
                {
                    int err = Marshal.GetLastWin32Error();

                    HostException e = CreateHostException(err, "RetreiveInputConsoleHandle",
                                                            ErrorCategory.ResourceUnavailable,
                                                            "The Win32 internal error \"{0}\" 0x{1:X} occurred while getting input about the console handle. Contact Microsoft Customer Support Services." ); //ConsoleControlStrings.GetInputModeExceptionTemplate);
                    throw e;
                }

                return new ConsoleHandle(handle, true);
            }
        );

        /// <summary>
        /// 
        /// Returns a ConsoleHandle to the console input device, even if stdin has been redirected.
        /// 
        /// </summary>
        /// <returns></returns>
        /// <exception cref="HostException">
        /// If Win32's CreateFile fails
        /// </exception>

        internal static ConsoleHandle GetInputHandle()
        {
            return _inputHandle.Value;
        }

        static readonly Lazy<ConsoleHandle> _outputHandle = new Lazy<SafeFileHandle>(() =>
            {
                // We use CreateFile here instead of GetStdWin32Handle, as GetStdWin32Handle will return redirected handles
                var handle = NativeMethods.CreateFile(
                    "CONOUT$",
                    (UInt32)(NativeMethods.AccessQualifiers.GenericRead | NativeMethods.AccessQualifiers.GenericWrite),
                    (UInt32)NativeMethods.ShareModes.ShareWrite,
                    (IntPtr)0,
                    (UInt32)NativeMethods.CreationDisposition.OpenExisting,
                    0,
                    (IntPtr)0);

                if (handle == NativeMethods.INVALID_HANDLE_VALUE)
                {
                    int err = Marshal.GetLastWin32Error();

                    HostException e = CreateHostException(err, "RetreiveActiveScreenBufferConsoleHandle",
                        ErrorCategory.ResourceUnavailable, "The Win32 internal error \"{0}\" 0x{1:X} occurred while retrieving the handle for the active console output buffer. Contact Microsoft Customer Support Services." ); //ConsoleControlStrings.GetActiveScreenBufferHandleExceptionTemplate);
                    throw e;
                }

                return new ConsoleHandle(handle, true);
            }
        );

        /// <summary>
        /// Returns a ConsoleHandle to the active screen buffer, even if that output has been redirected.
        /// </summary>
        /// <returns></returns>
        /// <exception cref="HostException">
        /// If Win32's CreateFile fails
        /// </exception>
        internal static ConsoleHandle GetActiveScreenBufferHandle()
        {
            return _outputHandle.Value;
        }

        internal enum StandardHandleId : uint
        {
            // From winnt.h
            // #define STD_INPUT_HANDLE    ((DWORD)-10)
            // #define STD_OUTPUT_HANDLE   ((DWORD)-11)
            // #define STD_ERROR_HANDLE    ((DWORD)-12)

            Input = 0xFFFFFFF6,
            Output = 0xFFFFFFF5,
            Error = 0xFFFFFFF4
        }

        internal static NakedWin32Handle GetStdHandle(StandardHandleId handleId)
        {
            unchecked
            {
                // Suppress the PreFAST warning about not using Marshal.GetLastWin32Error() to
                // get the error code.
#pragma warning disable 56523
                return NativeMethods.GetStdHandle((DWORD)handleId);
            }
        }



#endregion

#region Mode

        /// <summary>
        /// 
        /// flags used by ConsoleControl.GetMode and ConsoleControl.SetMode
        /// 
        /// </summary>
        [Flags]
        internal enum ConsoleModes : uint
        {
            // These values from wincon.h
            // input modes 
            ProcessedInput = 0x001,
            LineInput = 0x002,
            EchoInput = 0x004,
            WindowInput = 0x008,
            MouseInput = 0x010,
            Insert = 0x020,
            QuickEdit = 0x040,
            Extended = 0x080,
            AutoPosition = 0x100,
            // output modes
            ProcessedOutput = 0x001,  // yes, I know they are the same values as some flags defined above.
            WrapEndOfLine = 0x002,
            VirtualTerminal = 0x004,
        }

        /// <summary>
        ///
        /// Returns a mask of ConsoleModes flags describing the current modality of the console
        ///
        /// </summary>
        /// <exception cref="HostException">
        /// If Win32's GetConsoleMode fails
        /// </exception>

        internal static ConsoleModes GetMode(ConsoleHandle consoleHandle)
        {
            Util.Assert(!consoleHandle.IsInvalid, "consoleHandle is not valid");
            Util.Assert(!consoleHandle.IsClosed, "ConsoleHandle is closed");

            UInt32 m = 0;
            bool result = NativeMethods.GetConsoleMode(consoleHandle.DangerousGetHandle(), out m);

            if (result == false)
            {
                int err = Marshal.GetLastWin32Error();

                HostException e = CreateHostException(err, "GetConsoleMode",
                    ErrorCategory.ResourceUnavailable, "The Win32 internal error \"{0}\" 0x{1:X} occurred while getting the console mode. Contact Microsoft Customer Support Services." ); //ConsoleControlStrings.GetModeExceptionTemplate);
                throw e;
            }

            return (ConsoleModes)m;
        }

        /// <summary>
        /// 
        /// Sets the current mode of the console device
        /// 
        /// </summary>
        /// <param name="consoleHandle">
        /// 
        /// Handle to the console device returned by GetInputHandle
        /// 
        /// </param>
        /// <param name="mode">
        /// 
        /// Mask of mode flags
        /// 
        /// </param>
        /// <exception cref="HostException">
        /// 
        /// If Win32's SetConsoleMode fails
        /// 
        /// </exception>

        internal static void SetMode(ConsoleHandle consoleHandle, ConsoleModes mode)
        {
            Util.Assert(!consoleHandle.IsInvalid, "consoleHandle is not valid");
            Util.Assert(!consoleHandle.IsClosed, "ConsoleHandle is closed");

            bool result = NativeMethods.SetConsoleMode(consoleHandle.DangerousGetHandle(), (DWORD)mode);

            if (result == false)
            {
                int err = Marshal.GetLastWin32Error();

                HostException e = CreateHostException(err, "SetConsoleMode",
                    ErrorCategory.ResourceUnavailable, "The Win32 internal error \"{0}\" 0x{1:X} occurred while setting the console mode. Contact Microsoft Customer Support Services." ); //ConsoleControlStrings.SetModeExceptionTemplate);
                throw e;
            }
        }

        internal static bool CheckVirtualTerminalSupported()
        {
            var handle = GetActiveScreenBufferHandle();
            var m = GetMode( handle );
            if( NativeMethods.SetConsoleMode( handle.DangerousGetHandle(), (uint) (m | ConsoleControl.ConsoleModes.VirtualTerminal) ) )
            {
                // We only know if vt100 is supported if the previous call actually set the new flag, older
                // systems ignore the setting.
                m = GetMode( handle );
                if( (m & ConsoleModes.VirtualTerminal) != 0 )
                {
                    sm_colorWriter.VirtualTerminalSupported = true;
                    return true;
                }
            }
            return false;
        }

        #endregion

        #region Input



        /// <summary>
        ///
        /// Reads input from the console device according to the mode in effect (see GetMode, SetMode) 
        /// 
        /// </summary>
        /// <param name="consoleHandle"></param>
        /// 
        /// Handle to the console device returned by GetInputHandle
        /// 
        /// <param name="initialContent">
        /// 
        /// Initial contents of the edit buffer, if any. charactersToRead should be at least as large as the length of this string.
        /// 
        /// </param>
        /// <param name="charactersToRead">
        /// 
        /// Number of characters to read from the device.
        /// 
        /// </param>
        /// <param name="endOnTab">
        /// 
        /// true to allow the user to terminate input by hitting the tab or shift-tab key, in addition to the enter key
        /// 
        /// </param>
        /// <param name="keyState">
        /// 
        /// bit mask indicating the state of the control/shift keys at the point input was terminated.
        /// 
        /// </param>
        /// <returns></returns>
        /// <exception cref="HostException">
        /// 
        /// If Win32's ReadConsole fails
        /// 
        /// </exception>

        internal static string ReadConsole(ConsoleHandle consoleHandle, string initialContent,
            int charactersToRead, bool endOnTab, out uint keyState)
        {
            Util.Assert(!consoleHandle.IsInvalid, "ConsoleHandle is not valid");
            Util.Assert(!consoleHandle.IsClosed, "ConsoleHandle is closed");
            Util.Assert(initialContent != null, "if no initial content is desired, pass String.Empty");
            keyState = 0;

            CONSOLE_READCONSOLE_CONTROL control = new CONSOLE_READCONSOLE_CONTROL();

            control.nLength = (ULONG)Marshal.SizeOf(control);
            control.nInitialChars = (ULONG)initialContent.Length;
            control.dwControlKeyState = 0;
            if (endOnTab)
            {
                const int TAB = 0x9;

                control.dwCtrlWakeupMask = (1 << TAB);
            }

            DWORD charsReadUnused = 0;
            StringBuilder buffer = new StringBuilder(initialContent, charactersToRead);
            bool result =
                NativeMethods.ReadConsole(
                    consoleHandle.DangerousGetHandle(),
                    buffer,
                    (DWORD)charactersToRead,
                    out charsReadUnused,
                    ref control);
            keyState = control.dwControlKeyState;
            if (result == false)
            {
                int err = Marshal.GetLastWin32Error();

                HostException e = CreateHostException(err, "ReadConsole",
                    ErrorCategory.ReadError, "The Win32 internal error \"{0}\" 0x{1:X} occurred while reading characters from the console input buffer. Contact Microsoft Customer Support Services." ); //ConsoleControlStrings.ReadConsoleExceptionTemplate);
                throw e;
            }
            if (charsReadUnused > (uint)buffer.Length)
                charsReadUnused = (uint)buffer.Length;
            return buffer.ToString(0, (int)charsReadUnused);
        }

        /// <summary>
        /// Wraps Win32 ReadConsoleInput.
        /// Returns the number of records read in buffer.
        /// </summary>
        /// <param name="consoleHandle">
        /// 
        /// handle for the console where input is read
        /// 
        /// </param>
        /// <param name="buffer">
        /// 
        /// array where data read are stored
        /// 
        /// </param>
        /// <returns>
        /// 
        /// actual number of input records read
        /// 
        /// </returns>
        /// <exception cref="HostException">
        /// If Win32's ReadConsoleInput fails
        /// </exception>

        internal static int ReadConsoleInput(ConsoleHandle consoleHandle, ref INPUT_RECORD[] buffer)
        {
            Util.Assert(!consoleHandle.IsInvalid, "ConsoleHandle is not valid");
            Util.Assert(!consoleHandle.IsClosed, "ConsoleHandle is closed");

            DWORD recordsRead = 0;
            bool result =
                NativeMethods.ReadConsoleInput(
                    consoleHandle.DangerousGetHandle(),
                    buffer,
                    (DWORD)buffer.Length,
                    out recordsRead);
            if (result == false)
            {
                int err = Marshal.GetLastWin32Error();

                HostException e = CreateHostException(err, "ReadConsoleInput",
                    ErrorCategory.ReadError, "The Win32 internal error \"{0}\" 0x{1:X} occurred while reading input records from the console input buffer. Contact Microsoft Customer Support Services." ); //ConsoleControlStrings.ReadConsoleInputExceptionTemplate);
                throw e;
            }
            return (int)recordsRead;
        }

        /// <summary>
        /// Wraps Win32 PeekConsoleInput
        /// </summary>
        /// <param name="consoleHandle">
        /// 
        /// handle for the console where input is peeked
        /// 
        /// </param>
        /// <param name="buffer">
        /// 
        /// array where data read are stored
        /// 
        /// </param>
        /// <returns>
        /// 
        /// actual number of input records peeked
        /// 
        /// </returns>
        /// <exception cref="HostException">
        /// If Win32's PeekConsoleInput fails
        /// </exception>

        internal static int PeekConsoleInput
        (
            ConsoleHandle consoleHandle,
            ref INPUT_RECORD[] buffer
        )
        {
            Util.Assert(!consoleHandle.IsInvalid, "ConsoleHandle is not valid");
            Util.Assert(!consoleHandle.IsClosed, "ConsoleHandle is closed");

            DWORD recordsRead;
            bool result =
                NativeMethods.PeekConsoleInput(
                    consoleHandle.DangerousGetHandle(),
                    buffer,
                    (DWORD)buffer.Length,
                    out recordsRead);

            if (result == false)
            {
                int err = Marshal.GetLastWin32Error();

                HostException e = CreateHostException(err, "PeekConsoleInput",
                    ErrorCategory.ReadError, "The Win32 internal error \"{0}\" 0x{1:X} occurred while reading the contents of the console input buffer. Contact Microsoft Customer Support Services." ); //ConsoleControlStrings.PeekConsoleInputExceptionTemplate);
                throw e;
            }

            return (int)recordsRead;
        }

        /// <summary>
        /// Wraps Win32 GetNumberOfConsoleInputEvents
        /// </summary>
        /// <param name="consoleHandle">
        /// 
        /// handle for the console where the number of console input events is obtained
        /// 
        /// </param>
        /// <returns>
        /// 
        /// number of console input events
        /// 
        /// </returns>
        /// <exception cref="HostException">
        /// If Win32's GetNumberOfConsoleInputEvents fails
        /// </exception>

        internal static int GetNumberOfConsoleInputEvents(ConsoleHandle consoleHandle)
        {
            Util.Assert(!consoleHandle.IsInvalid, "ConsoleHandle is not valid");
            Util.Assert(!consoleHandle.IsClosed, "ConsoleHandle is closed");

            DWORD numEvents;
            bool result = NativeMethods.GetNumberOfConsoleInputEvents(consoleHandle.DangerousGetHandle(), out numEvents);

            if (result == false)
            {
                int err = Marshal.GetLastWin32Error();

                HostException e = CreateHostException(err, "GetNumberOfConsoleInputEvents",
                    ErrorCategory.ReadError, "The Win32 internal error \"{0}\" 0x{1:X} occurred while getting the number of events in the console input buffer. Contact Microsoft Customer Support Services." ); //ConsoleControlStrings.GetNumberOfConsoleInputEventsExceptionTemplate);
                throw e;
            }

            return (int)numEvents;
        }

        /// <summary>
        /// Wraps Win32 FlushConsoleInputBuffer
        /// </summary>
        /// <param name="consoleHandle">
        /// 
        /// handle for the console where the input buffer is flushed
        /// 
        /// </param>
        /// <exception cref="HostException">
        /// If Win32's FlushConsoleInputBuffer fails
        /// </exception>
        internal static void FlushConsoleInputBuffer(ConsoleHandle consoleHandle)
        {
            Util.Assert(!consoleHandle.IsInvalid, "ConsoleHandle is not valid");
            Util.Assert(!consoleHandle.IsClosed, "ConsoleHandle is closed");

            bool result = NativeMethods.FlushConsoleInputBuffer(consoleHandle.DangerousGetHandle());

            if (result == false)
            {
                int err = Marshal.GetLastWin32Error();

                HostException e = CreateHostException(err, "FlushConsoleInputBuffer",
                    ErrorCategory.ReadError, "The Win32 internal error \"{0}\" 0x{1:X} occurred while flushing the console input buffer. Contact Microsoft Customer Support Services." ); //ConsoleControlStrings.FlushConsoleInputBufferExceptionTemplate);
                throw e;
            }
        }

#endregion Input

#region Buffer

        /// <summary>
        /// Wraps Win32 GetConsoleScreenBufferInfo
        /// Returns Console Screen Buffer Info
        /// </summary>
        /// <param name="consoleHandle">
        /// 
        /// Handle for the console where the screen buffer info is obtained
        /// 
        /// </param>
        /// <returns>
        /// 
        /// info about the screen buffer. See the definition of CONSOLE_SCREEN_BUFFER_INFO
        /// 
        /// </returns>
        /// <exception cref="HostException">
        /// If Win32's GetConsoleScreenBufferInfo fails
        /// </exception>
        internal static CONSOLE_SCREEN_BUFFER_INFO GetConsoleScreenBufferInfo(ConsoleHandle consoleHandle)
        {
            Util.Assert(!consoleHandle.IsInvalid, "ConsoleHandle is not valid");
            Util.Assert(!consoleHandle.IsClosed, "ConsoleHandle is closed");

            CONSOLE_SCREEN_BUFFER_INFO bufferInfo;
            bool result = NativeMethods.GetConsoleScreenBufferInfo(consoleHandle.DangerousGetHandle(), out bufferInfo);

            if (result == false)
            {
                int err = Marshal.GetLastWin32Error();

                HostException e = CreateHostException(err, "GetConsoleScreenBufferInfo",
                    ErrorCategory.ResourceUnavailable, "The Win32 internal error \"{0}\" 0x{1:X} occurred while getting console output buffer information. Contact Microsoft Customer Support Services." ); //ConsoleControlStrings.GetConsoleScreenBufferInfoExceptionTemplate);
                throw e;
            }

            return bufferInfo;
        }

        /// <summary>
        /// set the output buffer's size
        /// </summary>
        /// <param name="consoleHandle"></param>
        /// <param name="newSize"></param>
        /// <exception cref="HostException">
        /// If Win32's SetConsoleScreenBufferSize fails
        /// </exception>

        internal static void SetConsoleScreenBufferSize(ConsoleHandle consoleHandle, Size newSize)
        {
            Util.Assert(!consoleHandle.IsInvalid, "ConsoleHandle is not valid");
            Util.Assert(!consoleHandle.IsClosed, "ConsoleHandle is closed");

            COORD s;

            s.X = (short)newSize.Width;
            s.Y = (short)newSize.Height;

            bool result = NativeMethods.SetConsoleScreenBufferSize(consoleHandle.DangerousGetHandle(), s);

            if (result == false)
            {
                int err = Marshal.GetLastWin32Error();

                HostException e = CreateHostException(err, "SetConsoleScreenBufferSize",
                    ErrorCategory.ResourceUnavailable, "The Win32 internal error \"{0}\" 0x{1:X} occurred while setting the console output buffer size. Contact Microsoft Customer Support Services." ); //ConsoleControlStrings.SetConsoleScreenBufferSizeExceptionTemplate);
                throw e;
            }
        }

        internal static bool IsConsoleColor(ConsoleColor c)
        {
            switch (c)
            {
                case ConsoleColor.Black:
                case ConsoleColor.Blue:
                case ConsoleColor.Cyan:
                case ConsoleColor.DarkBlue:
                case ConsoleColor.DarkCyan:
                case ConsoleColor.DarkGray:
                case ConsoleColor.DarkGreen:
                case ConsoleColor.DarkMagenta:
                case ConsoleColor.DarkRed:
                case ConsoleColor.DarkYellow:
                case ConsoleColor.Gray:
                case ConsoleColor.Green:
                case ConsoleColor.Magenta:
                case ConsoleColor.Red:
                case ConsoleColor.White:
                case ConsoleColor.Yellow:
                    return true;
            }
            return false;
        }

        internal static void WORDToColor(WORD attribute, out ConsoleColor foreground, out ConsoleColor background)
        {
            // foreground color is the low-byte in the word, background color is the hi-byte.
            foreground = (ConsoleColor)(attribute & 0x0f);
            background = (ConsoleColor)((attribute & 0xf0) >> 4);
            Util.Assert(IsConsoleColor(foreground), "unknown color");
            Util.Assert(IsConsoleColor(background), "unknown color");
        }

        internal static WORD ColorToWORD(ConsoleColor foreground, ConsoleColor background)
        {
            WORD result = (WORD)(((int)background << 4) | (int)foreground);

            return result;
        }

        /// <summary>
        /// Wrap32 WriteConsoleOutput.
        /// This wrapper is not limited to 64K or 8K CHAR_INFO to which Win32's WriteConsoleOutput
        /// is constrained.
        /// </summary>
        /// <param name="consoleHandle">
        /// 
        /// handle for the console where output is written
        /// 
        /// </param>
        /// <param name="origin">
        /// 
        /// location on screen buffer where writing starts
        /// 
        /// </param>
        /// <param name="contents">
        /// 
        /// 2D array of cells. Caller needs to ensure that the array is 2D.
        /// 
        /// </param>
        /// <exception cref="HostException">
        /// If Win32's GetConsoleScreenBufferInfo fails
        /// If there is not enough memory to complete calls to Win32's WriteConsoleOutput
        /// </exception>
        /// <exception cref="ArgumentNullException">
        /// If <paramref name="contents"/> is null
        /// </exception>
        /// <exception cref="ArgumentException">
        /// If it is illegal to write <paramref name="contents"/> to the output buffer 
        /// </exception>

        internal static void WriteConsoleOutput(ConsoleHandle consoleHandle, Coordinates origin, BufferCell[,] contents)
        {
            Util.Assert(!consoleHandle.IsInvalid, "ConsoleHandle is not valid");
            Util.Assert(!consoleHandle.IsClosed, "ConsoleHandle is closed");
            if (contents == null)
            {
                throw new ArgumentNullException( "contents" );
            }
            uint codePage;
            if (IsCJKOutputCodePage(out codePage))
            {
                // contentsRegion indicates the area in contents (declared below) in which
                // the data read from ReadConsoleOutput is stored.
                Rectangle contentsRegion = new Rectangle();
                ConsoleControl.CONSOLE_SCREEN_BUFFER_INFO bufferInfo =
                    GetConsoleScreenBufferInfo(consoleHandle);

                int bufferWidth = bufferInfo.BufferSize.X;
                int bufferHeight = bufferInfo.BufferSize.Y;
                Rectangle screenRegion = new Rectangle(
                    origin.X, origin.Y,
                    Math.Min(origin.X + contents.GetLength(1) - 1, bufferWidth - 1),
                    Math.Min(origin.Y + contents.GetLength(0) - 1, bufferHeight - 1));

                contentsRegion.Left = contents.GetLowerBound(1);
                contentsRegion.Top = contents.GetLowerBound(0);
                contentsRegion.Right = contentsRegion.Left +
                    screenRegion.Right - screenRegion.Left;
                contentsRegion.Bottom = contentsRegion.Top +
                    screenRegion.Bottom - screenRegion.Top;

#if DEBUG
                //Check contents in contentsRegion
                CheckWriteConsoleOutputContents(contents, contentsRegion);
#endif

                //Identify edges and areas of identical contiguous edges in contentsRegion
                List<BufferCellArrayRowTypeRange> sameEdgeAreas = new List<BufferCellArrayRowTypeRange>();
                int firstLeftTrailingRow = -1, firstRightLeadingRow = -1;
                BuildEdgeTypeInfo(contentsRegion, contents,
                    sameEdgeAreas, out firstLeftTrailingRow, out firstRightLeadingRow);

#if DEBUG
                CheckWriteEdges(consoleHandle, codePage, origin, contents, contentsRegion,
                    bufferInfo, firstLeftTrailingRow, firstRightLeadingRow);
#endif

                foreach (BufferCellArrayRowTypeRange area in sameEdgeAreas)
                {
                    Coordinates o = new Coordinates(origin.X,
                                                    origin.Y + area.Start - contentsRegion.Top);
                    Rectangle contRegion = new Rectangle(
                        contentsRegion.Left, area.Start, contentsRegion.Right, area.End);
                    if ((area.Type & BufferCellArrayRowType.LeftTrailing) != 0)
                    {
                        contRegion.Left++;
                        o.X++;
                        if (o.X >= bufferWidth || contRegion.Right < contRegion.Left)
                        {
                            return;
                        }
                    }

                    WriteConsoleOutputCJK(consoleHandle, o, contRegion, contents, area.Type);
                }
            }
            else
            {
                WriteConsoleOutputPlain(consoleHandle, origin, contents);
            }
        }

        private static void BuildEdgeTypeInfo(
            Rectangle contentsRegion,
            BufferCell[,] contents,
            List<BufferCellArrayRowTypeRange> sameEdgeAreas,
            out int firstLeftTrailingRow,
            out int firstRightLeadingRow)
        {
            firstLeftTrailingRow = -1;
            firstRightLeadingRow = -1;
            BufferCellArrayRowType edgeType =
                GetEdgeType(contents[contentsRegion.Top, contentsRegion.Left],
                    contents[contentsRegion.Top, contentsRegion.Right]);
            for (int r = contentsRegion.Top; r <= contentsRegion.Bottom;)
            {
                BufferCellArrayRowTypeRange range;
                range.Start = r;
                range.Type = edgeType;
                if (firstLeftTrailingRow == -1 && ((range.Type & BufferCellArrayRowType.LeftTrailing) != 0))
                {
                    firstLeftTrailingRow = r;
                }
                if (firstRightLeadingRow == -1 && ((range.Type & BufferCellArrayRowType.RightLeading) != 0))
                {
                    firstRightLeadingRow = r;
                }
                for (;;)
                {
                    r++;
                    if (r > contentsRegion.Bottom)
                    {
                        range.End = r - 1;
                        sameEdgeAreas.Add(range);
                        return;
                    }
                    edgeType = GetEdgeType(contents[r, contentsRegion.Left], contents[r, contentsRegion.Right]);
                    if (edgeType != range.Type)
                    {
                        range.End = r - 1;
                        sameEdgeAreas.Add(range);
                        break;
                    }
                }
            }
        }

        private static BufferCellArrayRowType GetEdgeType(BufferCell left, BufferCell right)
        {
            BufferCellArrayRowType edgeType = 0;
            if (left.BufferCellType == BufferCellType.Trailing)
            {
                edgeType |= BufferCellArrayRowType.LeftTrailing;
            }
            if (right.BufferCellType == BufferCellType.Leading)
            {
                edgeType |= BufferCellArrayRowType.RightLeading;
            }
            return edgeType;
        }

        private struct BufferCellArrayRowTypeRange
        {
            internal int Start;
            internal int End;
            internal BufferCellArrayRowType Type;
        }

        [Flags]
        private enum BufferCellArrayRowType : uint
        {
            LeftTrailing = 0x1,
            RightLeading = 0x2
        }

        /// <summary>
        /// Check the existing screen columns left and right of areas to be written
        /// </summary>
        /// <param name="consoleHandle"></param>
        /// <param name="codePage"></param>
        /// <param name="origin">must be within the screen buffer</param>
        /// <param name="contents"></param>
        /// <param name="contentsRegion"></param>
        /// <param name="bufferInfo"></param>
        /// <param name="firstLeftTrailingRow"></param>
        /// <param name="firstRightLeadingRow"></param>
        /// <exception cref="ArgumentException">
        /// If it is illegal to write <paramref name="contents"/> at <paramref name="origin"/>
        /// </exception>
        /// <exception cref="HostException">
        /// If there is not enough memory to complete calls to Win32's ReadConsoleOutput
        /// </exception>
        internal static void CheckWriteEdges(
            ConsoleHandle consoleHandle,
            uint codePage, Coordinates origin,
            BufferCell[,] contents,
            Rectangle contentsRegion,
            ConsoleControl.CONSOLE_SCREEN_BUFFER_INFO bufferInfo,
            int firstLeftTrailingRow,
            int firstRightLeadingRow)
        {
            Rectangle existingRegion = new Rectangle(0, 0, 1, contentsRegion.Bottom - contentsRegion.Top);
            if (origin.X == 0)
            {
                if (firstLeftTrailingRow >= 0)
                {
                    throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "contents[{0}, {1}]",
                        firstLeftTrailingRow, contentsRegion.Left));
                }
            }
            else
            {
                // use ReadConsoleOutputCJK because checking the left and right edges of the existing output
                // is NOT needed
                BufferCell[,] leftExisting = new BufferCell[existingRegion.Bottom + 1, 2];
                ReadConsoleOutputCJK(consoleHandle, codePage,
                    new Coordinates(origin.X - 1, origin.Y), existingRegion, ref leftExisting);
                for (int r = contentsRegion.Top, i = 0; r <= contentsRegion.Bottom; r++, i++)
                {
                    if (leftExisting[r, 0].BufferCellType == BufferCellType.Leading ^
                            contents[r, contentsRegion.Left].BufferCellType == BufferCellType.Trailing)
                    {
                        throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "contents[{0}, {1}]",
                            r, contentsRegion.Left));
                    }
                }
            }
            //Check right edge
            if (origin.X + (contentsRegion.Right - contentsRegion.Left) + 1 >= bufferInfo.BufferSize.X)
            {
                if (firstRightLeadingRow >= 0)
                {
                    throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "contents[{0}, {1}]",
                            firstRightLeadingRow, contentsRegion.Right));
                }
            }
            else
            {
                // use ReadConsoleOutputCJK because checking the left and right edges of the existing output
                // is NOT needed
                BufferCell[,] rightExisting = new BufferCell[existingRegion.Bottom + 1, 2];
                ReadConsoleOutputCJK(consoleHandle, codePage,
                    new Coordinates(origin.X + (contentsRegion.Right - contentsRegion.Left), origin.Y), existingRegion, ref rightExisting);
                for (int r = contentsRegion.Top, i = 0; r <= contentsRegion.Bottom; r++, i++)
                {
                    if (rightExisting[r, 0].BufferCellType == BufferCellType.Leading ^
                            contents[r, contentsRegion.Right].BufferCellType == BufferCellType.Leading)
                    {
                        throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "contents[{0}, {1}]",
                            r, contentsRegion.Right));
                    }
                }
            }
        }


        private static void CheckWriteConsoleOutputContents(BufferCell[,] contents, Rectangle contentsRegion)
        {
            for (int r = contentsRegion.Top; r <= contentsRegion.Bottom; r++)
            {
                for (int c = contentsRegion.Left; c <= contentsRegion.Right; c++)
                {
                    // Changes have been made in the following code such that 2 cell characters
                    // (Chinese, Japanese or Korean) can be in a single BufferCell structure
                    // which is complete
                    if (contents[r, c].BufferCellType == BufferCellType.Trailing &&
                        contents[r, c].Character != 0)
                    {
                        // trailing character is not 0
                        throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "contents[{0}, {1}]", r, c));
                    }

                    if (contents[r, c].BufferCellType == BufferCellType.Leading)
                    {
                        c++;
                        if (c > contentsRegion.Right)
                        {
                            break;
                        }
                        if (contents[r, c].Character != 0 || contents[r, c].BufferCellType != BufferCellType.Trailing)
                        {
                            // for a 2 cell character, either there is no trailing BufferCell or 
                            // the trailing BufferCell's character is not 0
                            throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "contents[{0}, {1}]", r, c));
                        }
                    }
                }
            }
        }

        private static void WriteConsoleOutputCJK(ConsoleHandle consoleHandle, Coordinates origin, Rectangle contentsRegion, BufferCell[,] contents, BufferCellArrayRowType rowType)
        {
            Util.Assert(origin.X >= 0 && origin.Y >= 0,
                "origin must be within the output buffer");
            int rows = contentsRegion.Bottom - contentsRegion.Top + 1;
            int cols = contentsRegion.Right - contentsRegion.Left + 1;

            CONSOLE_FONT_INFO_EX fontInfo = GetConsoleFontInfo(consoleHandle);
            int fontType = fontInfo.FontFamily & NativeMethods.FontTypeMask;
            bool trueTypeInUse = (fontType & NativeMethods.TrueTypeFont) == NativeMethods.TrueTypeFont;

            int bufferLimit = 2 * 1024; // Limit is 8K bytes as each CHAR_INFO takes 4 bytes

            COORD bufferCoord;

            bufferCoord.X = 0;
            bufferCoord.Y = 0;

            // keeps track of which screen area write
            SMALL_RECT writeRegion;

            writeRegion.Top = (short)origin.Y;

            int rowsRemaining = rows;

            while (rowsRemaining > 0)
            {
                // Iteration of columns is nested inside iteration of rows.
                // If the size of contents exceeds the buffer limit, writing is
                // done in blocks of size equal to the bufferlimit from left to right
                // then top to bottom.
                // For each iteration of rows,
                // - writeRegion.Left and bufferSize.X are reset
                // - rowsRemaining, writeRegion.Top, writeRegion.Bottom, and bufferSize.Y
                //     are updated
                //   For each iteration of columns,
                //   - writeRegion.Left, writeRegion.Right and bufferSize.X are updated

                writeRegion.Left = (short)origin.X;

                COORD bufferSize;

                bufferSize.X = (short)Math.Min(cols, bufferLimit);
                bufferSize.Y = (short)Math.Min
                                        (
                                            rowsRemaining,
                                            bufferLimit / bufferSize.X
                                        );
                writeRegion.Bottom = (short)(writeRegion.Top + bufferSize.Y - 1);

                // atRow is at which row of contents a particular iteration is operating
                int atRow = rows - rowsRemaining + contentsRegion.Top;

                // number of columns yet to be written
                int colsRemaining = cols;
                while (colsRemaining > 0)
                {
                    writeRegion.Right = (short)(writeRegion.Left + bufferSize.X - 1);

                    // atCol is at which column of contents a particular iteration is operating
                    int atCol = cols - colsRemaining + contentsRegion.Left;
                    // if this is not the last column iteration &&
                    //   the leftmost BufferCell is a leading cell, don't write that cell
                    if (colsRemaining > bufferSize.X &&
                         contents[atRow, atCol + bufferSize.X - 1].BufferCellType == BufferCellType.Leading)
                    {
                        bufferSize.X--;
                        writeRegion.Right--;
                    }
                    CHAR_INFO[] characterBuffer = new CHAR_INFO[bufferSize.Y * bufferSize.X];

                    // copy characterBuffer to contents;
                    int characterBufferIndex = 0;
                    bool lastCharIsLeading = false;
                    BufferCell lastLeadingCell = new BufferCell();
                    for (int r = atRow; r < bufferSize.Y + atRow; r++)
                    {
                        for (int c = atCol; c < bufferSize.X + atCol; c++, characterBufferIndex++)
                        {
                            if (contents[r, c].BufferCellType == BufferCellType.Complete)
                            {
                                characterBuffer[characterBufferIndex].UnicodeChar =
                                    (ushort)contents[r, c].Character;
                                characterBuffer[characterBufferIndex].Attributes =
                                    (ushort)(ColorToWORD(contents[r, c].ForegroundColor, contents[r, c].BackgroundColor));

                                lastCharIsLeading = false;
                            }
                            else if (contents[r, c].BufferCellType == BufferCellType.Leading)
                            {
                                characterBuffer[characterBufferIndex].UnicodeChar =
                                    (ushort)contents[r, c].Character;
                                characterBuffer[characterBufferIndex].Attributes =
                                    (ushort)(ColorToWORD(contents[r, c].ForegroundColor, contents[r, c].BackgroundColor)
                                                | (ushort)NativeMethods.CHAR_INFO_Attributes.COMMON_LVB_LEADING_BYTE);

                                lastCharIsLeading = true;
                                lastLeadingCell = contents[r, c];
                            }
                            else if (contents[r, c].BufferCellType == BufferCellType.Trailing)
                            {
                                // The FontFamily is a 8-bit integer. The low-order bit (bit 0) specifies the pitch of the font. 
                                // If it is 1, the font is variable pitch (or proportional). If it is 0, the font is fixed pitch 
                                // (or monospace). Bits 1 and 2 specify the font type. If both bits are 0, the font is a raster font; 
                                // if bit 1 is 1 and bit 2 is 0, the font is a vector font; if bit 1 is 0 and bit 2 is set, or if both 
                                // bits are 1, the font is true type. Bit 3 is 1 if the font is a device font; otherwise, it is 0.
                                // We only care about the bit 1 and 2, which indicate the font type.
                                // There are only two font type defined for the Console, at
                                // HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Console\.
                                //     Console\Nls           --- national language supports
                                //     Console\RasterFonts   --- raster type font
                                //     Console\TrueTypeFont  --- true type font
                                // For CJK characters, if it's TrueType, we need to output the trailing character marked with "Trailing_byte"
                                // attribute. But if it's RasterFont, we ignore the trailing character, and the "Leading_byte"/"Trailing_byte"
                                // attributes are not effective at all when reading the character from the console buffer.
                                if (lastCharIsLeading && trueTypeInUse)
                                {
                                    // For TrueType Font, we output the trailing byte with "Trailing_byte" attribute
                                    characterBuffer[characterBufferIndex].UnicodeChar = lastLeadingCell.Character;
                                    characterBuffer[characterBufferIndex].Attributes =
                                        (ushort)(ColorToWORD(contents[r, c].ForegroundColor, contents[r, c].BackgroundColor)
                                                | (ushort)NativeMethods.CHAR_INFO_Attributes.COMMON_LVB_TRAILING_BYTE);
                                }
                                else
                                {
                                    // We don't output anything for this cell if Raster font is in use, or if the last cell is not a leading byte
                                    characterBufferIndex--;
                                }
                                lastCharIsLeading = false;
                            }
                        }
                    }

                    // Now writeRegion, bufferSize and characterBuffer are updated.
                    // Call NativeMethods.WriteConsoleOutput
                    bool result;
                    if ((rowType & BufferCellArrayRowType.RightLeading) != 0 &&
                            colsRemaining == bufferSize.X)
                    {
                        COORD bSize = bufferSize;
                        bSize.X++;
                        SMALL_RECT wRegion = writeRegion;
                        wRegion.Right++;
                        // Suppress the PreFAST warning about not using Marshal.GetLastWin32Error() to
                        // get the error code.
#pragma warning disable 56523
                        result = NativeMethods.WriteConsoleOutput(
                            consoleHandle.DangerousGetHandle(),
                            characterBuffer,
                            bSize,
                            bufferCoord,
                            ref wRegion);
                    }
                    else
                    {
                        // Suppress the PreFAST warning about not using Marshal.GetLastWin32Error() to
                        // get the error code.
#pragma warning disable 56523
                        result = NativeMethods.WriteConsoleOutput(
                            consoleHandle.DangerousGetHandle(),
                            characterBuffer,
                            bufferSize,
                            bufferCoord,
                            ref writeRegion);
                    }
                    if (result == false)
                    {
                        // When WriteConsoleOutput fails, half bufferLimit
                        if (bufferLimit < 2)
                        {
                            int err = Marshal.GetLastWin32Error();
                            HostException e = CreateHostException(err, "WriteConsoleOutput",
                                ErrorCategory.WriteError, "The Win32 internal error \"{0}\" 0x{1:X} occurred while writing to the console output buffer. Contact Microsoft Customer Support Services." ); //ConsoleControlStrings.WriteConsoleOutputExceptionTemplate);
                            throw e;
                        }
                        bufferLimit /= 2;
                        if (cols == colsRemaining)
                        {
                            // if cols == colsRemaining, nothing is guaranteed written in this pass and
                            //  the unwritten area is still rectangular
                            bufferSize.Y = 0;
                            break;
                        }
                        else
                        {
                            // some areas have been written. This could only happen when the number of columns
                            // to write is larger than bufferLimit. In that case, the algorithm writes one row
                            // at a time => bufferSize.Y == 1. Then, we can safely leave bufferSize.Y unchanged
                            // to retry with a smaller bufferSize.X.
                            Util.Assert(bufferSize.Y == 1, string.Format(CultureInfo.InvariantCulture, "bufferSize.Y should be 1, but is {0}", bufferSize.Y));
                            bufferSize.X = (short)Math.Min(colsRemaining, bufferLimit);
                            continue;
                        }
                    }

                    colsRemaining -= bufferSize.X;
                    writeRegion.Left += bufferSize.X;
                    bufferSize.X = (short)Math.Min(colsRemaining, bufferLimit);
                }  // column iteration

                rowsRemaining -= bufferSize.Y;
                writeRegion.Top += bufferSize.Y;
            }  // row iteration
        }

        private static void WriteConsoleOutputPlain(ConsoleHandle consoleHandle, Coordinates origin, BufferCell[,] contents)
        {
            int rows = contents.GetLength(0);
            int cols = contents.GetLength(1);

            if ((rows <= 0) || cols <= 0)
            {
                //tracer.WriteLine("contents passed in has 0 rows and columns");
                return;
            }
            int bufferLimit = 2 * 1024; // Limit is 8K bytes as each CHAR_INFO takes 4 bytes

            COORD bufferCoord;

            bufferCoord.X = 0;
            bufferCoord.Y = 0;

            // keeps track of which screen area write
            SMALL_RECT writeRegion;

            writeRegion.Top = (short)origin.Y;

            int rowsRemaining = rows;

            while (rowsRemaining > 0)
            {
                // Iteration of columns is nested inside iteration of rows.
                // If the size of contents exceeds the buffer limit, writing is
                // done in blocks of size equal to the bufferlimit from left to right
                // then top to bottom.
                // For each iteration of rows,
                // - writeRegion.Left and bufferSize.X are reset
                // - rowsRemaining, writeRegion.Top, writeRegion.Bottom, and bufferSize.Y
                //     are updated
                //   For each iteration of columns,
                //   - writeRegion.Left, writeRegion.Right and bufferSize.X are updated

                writeRegion.Left = (short)origin.X;

                COORD bufferSize;

                bufferSize.X = (short)Math.Min(cols, bufferLimit);
                bufferSize.Y = (short)Math.Min
                                        (
                                            rowsRemaining,
                                            bufferLimit / bufferSize.X
                                        );
                writeRegion.Bottom = (short)(writeRegion.Top + bufferSize.Y - 1);

                // atRow is at which row of contents a particular iteration is operating
                int atRow = rows - rowsRemaining + contents.GetLowerBound(0);

                // number of columns yet to be written
                int colsRemaining = cols;

                while (colsRemaining > 0)
                {
                    writeRegion.Right = (short)(writeRegion.Left + bufferSize.X - 1);

                    // atCol is at which column of contents a particular iteration is operating
                    int atCol = cols - colsRemaining + contents.GetLowerBound(1);
                    CHAR_INFO[] characterBuffer = new CHAR_INFO[bufferSize.Y * bufferSize.X];

                    // copy characterBuffer to contents;
                    for (int r = atRow, characterBufferIndex = 0;
                        r < bufferSize.Y + atRow; r++)
                    {
                        for (int c = atCol; c < bufferSize.X + atCol; c++, characterBufferIndex++)
                        {
                            characterBuffer[characterBufferIndex].UnicodeChar =
                                (ushort)contents[r, c].Character;
                            characterBuffer[characterBufferIndex].Attributes =
                                ColorToWORD(contents[r, c].ForegroundColor, contents[r, c].BackgroundColor);
                        }
                    }

                    // Now writeRegion, bufferSize and characterBuffer are updated.
                    // Call NativeMethods.WriteConsoleOutput
                    bool result =
                        NativeMethods.WriteConsoleOutput(
                            consoleHandle.DangerousGetHandle(),
                            characterBuffer,
                            bufferSize,
                            bufferCoord,
                            ref writeRegion);

                    if (result == false)
                    {
                        // When WriteConsoleOutput fails, half bufferLimit
                        if (bufferLimit < 2)
                        {
                            int err = Marshal.GetLastWin32Error();
                            HostException e = CreateHostException(err, "WriteConsoleOutput",
                                ErrorCategory.WriteError, "The Win32 internal error \"{0}\" 0x{1:X} occurred while writing to the console output buffer. Contact Microsoft Customer Support Services." ); //ConsoleControlStrings.WriteConsoleOutputExceptionTemplate);
                            throw e;
                        }
                        bufferLimit /= 2;
                        if (cols == colsRemaining)
                        {
                            // if cols == colsRemaining, nothing is guaranteed written in this pass and
                            //  the unwritten area is still rectangular
                            bufferSize.Y = 0;
                            break;
                        }
                        else
                        {
                            // some areas have been written. This could only happen when the number of columns
                            // to write is larger than bufferLimit. In that case, the algorithm writes one row
                            // at a time => bufferSize.Y == 1. Then, we can safely leave bufferSize.Y unchanged
                            // to retry with a smaller bufferSize.X.
                            Util.Assert(bufferSize.Y == 1, string.Format(CultureInfo.InvariantCulture, "bufferSize.Y should be 1, but is {0}", bufferSize.Y));
                            bufferSize.X = (short)Math.Min(colsRemaining, bufferLimit);
                            continue;
                        }
                    }

                    colsRemaining -= bufferSize.X;
                    writeRegion.Left += bufferSize.X;
                    bufferSize.X = (short)Math.Min(colsRemaining, bufferLimit);
                }  // column iteration

                rowsRemaining -= bufferSize.Y;
                writeRegion.Top += bufferSize.Y;
            }  // row iteration
        }

        /// <summary>
        /// Wrap32 ReadConsoleOutput
        /// This wrapper is not limited to 64K or 8K CHAR_INFO to which Win32's ReadConsoleOutput
        /// is constrained.
        /// </summary>
        /// <param name="consoleHandle">
        /// 
        /// handle for the console where output is read
        /// 
        /// </param>
        /// <param name="origin">
        /// 
        /// location on screen buffer where reading begins
        /// 
        /// </param>
        /// <param name="contentsRegion">
        /// 
        /// indicates the area in <paramref name="contents"/> where the data read 
        /// is stored.
        /// 
        /// </param>
        /// <param name="contents">
        /// 
        /// this is ref because the bounds and size of the array are needed.
        /// 
        /// </param>
        /// <exception cref="HostException">
        /// If there is not enough memory to complete calls to Win32's ReadConsoleOutput
        /// </exception>

        internal static void ReadConsoleOutput
        (
            ConsoleHandle consoleHandle,
            Coordinates origin,
            Rectangle contentsRegion,
            ref BufferCell[,] contents
        )
        {
            Util.Assert(!consoleHandle.IsInvalid, "ConsoleHandle is not valid");
            Util.Assert(!consoleHandle.IsClosed, "ConsoleHandle is closed");
            uint codePage;
            if (IsCJKOutputCodePage(out codePage))
            {
                ReadConsoleOutputCJK(consoleHandle, codePage, origin, contentsRegion, ref contents);
                // check left edge
                BufferCell[,] cellArray = null;
                Coordinates checkOrigin;
                Rectangle cellArrayRegion = new Rectangle(0, 0, 1, contentsRegion.Bottom - contentsRegion.Top);
                if (origin.X > 0 && ShouldCheck(contentsRegion.Left, contents, contentsRegion))
                {
                    cellArray = new BufferCell[cellArrayRegion.Bottom + 1, 2];
                    checkOrigin = new Coordinates(origin.X - 1, origin.Y);
                    ReadConsoleOutputCJK(consoleHandle, codePage, checkOrigin,
                        cellArrayRegion, ref cellArray);
                    for (int i = 0; i <= cellArrayRegion.Bottom; i++)
                    {
                        if (cellArray[i, 0].BufferCellType == BufferCellType.Leading)
                        {
                            contents[contentsRegion.Top + i, 0].Character = (char)0;
                            contents[contentsRegion.Top + i, 0].BufferCellType = BufferCellType.Trailing;
                        }
                    }
                }

                // check right edge
                ConsoleControl.CONSOLE_SCREEN_BUFFER_INFO bufferInfo =
                    GetConsoleScreenBufferInfo(consoleHandle);
                if (origin.X + (contentsRegion.Right - contentsRegion.Left) + 1 < bufferInfo.BufferSize.X &&
                    ShouldCheck(contentsRegion.Right, contents, contentsRegion))
                {
                    if (cellArray == null)
                    {
                        cellArray = new BufferCell[cellArrayRegion.Bottom + 1, 2];
                    }
                    checkOrigin = new Coordinates(origin.X +
                        (contentsRegion.Right - contentsRegion.Left), origin.Y);
                    ReadConsoleOutputCJK(consoleHandle, codePage, checkOrigin,
                        cellArrayRegion, ref cellArray);
                    for (int i = 0; i <= cellArrayRegion.Bottom; i++)
                    {
                        if (cellArray[i, 0].BufferCellType == BufferCellType.Leading)
                        {
                            contents[contentsRegion.Top + i, contentsRegion.Right] = cellArray[i, 0];
                        }
                    }
                }
            }
            else
            {
                ReadConsoleOutputPlain(consoleHandle, origin, contentsRegion, ref contents);
            }
        }

#region ReadConsoleOutput CJK
        /// <summary>
        /// If an edge cell read is a blank, it is potentially part of a double width character. Hence,
        ///  at least one of the left and right edges should be checked
        /// </summary>
        /// <param name="edge"></param>
        /// <param name="contents"></param>
        /// <param name="contentsRegion"></param>
        /// <returns></returns>
        private static bool ShouldCheck(int edge, BufferCell[,] contents, Rectangle contentsRegion)
        {
            for (int i = contentsRegion.Top; i <= contentsRegion.Bottom; i++)
            {
                if (contents[i, edge].Character == ' ')
                {
                    return true;
                }
            }
            return false;
        }

        private static bool ReadConsoleOutputCJKSmall
        (
            ConsoleHandle consoleHandle,
            uint codePage,
            Coordinates origin,
            Rectangle contentsRegion,
            ref BufferCell[,] contents
        )
        {
            COORD bufferSize;
            bufferSize.X = (short)(contentsRegion.Right - contentsRegion.Left + 1);
            bufferSize.Y = (short)(contentsRegion.Bottom - contentsRegion.Top + 1);
            COORD bufferCoord;
            bufferCoord.X = 0;
            bufferCoord.Y = 0;
            CHAR_INFO[] characterBuffer = new CHAR_INFO[bufferSize.X * bufferSize.Y];
            SMALL_RECT readRegion;
            readRegion.Left = (short)origin.X;
            readRegion.Top = (short)origin.Y;
            readRegion.Right = (short)(origin.X + bufferSize.X - 1);
            readRegion.Bottom = (short)(origin.Y + bufferSize.Y - 1);
            // Suppress the PreFAST warning about not using Marshal.GetLastWin32Error() to
            // get the error code.
#pragma warning disable 56523
            bool result = NativeMethods.ReadConsoleOutput(
                                        consoleHandle.DangerousGetHandle(),
                                        characterBuffer,
                                        bufferSize,
                                        bufferCoord,
                                        ref readRegion);
            if (!result)
            {
                return false;
            }
            int characterBufferIndex = 0;

            for (int r = contentsRegion.Top; r <= contentsRegion.Bottom; r++)
            {
                for (int c = contentsRegion.Left; c <= contentsRegion.Right; c++, characterBufferIndex++)
                {
                    ConsoleColor fgColor, bgColor;

                    contents[r, c].Character = (char)characterBuffer[characterBufferIndex].UnicodeChar;
                    WORDToColor(characterBuffer[characterBufferIndex].Attributes,
                                out fgColor,
                                out bgColor);
                    contents[r, c].ForegroundColor = fgColor;
                    contents[r, c].BackgroundColor = bgColor;

                    // Set the attributes of the buffercells to be the same as that of the 
                    // incoming CHAR_INFO. In case where the CHAR_INFO character is a 
                    // trailing byte set the Character of BufferCell to 0. This is done
                    // because at a lot of places this check is being done. Having a trailing
                    // character to be 0 is by design.

                    if ((characterBuffer[characterBufferIndex].Attributes & (ushort)NativeMethods.CHAR_INFO_Attributes.COMMON_LVB_LEADING_BYTE)
                            == (ushort)NativeMethods.CHAR_INFO_Attributes.COMMON_LVB_LEADING_BYTE)
                    {
                        contents[r, c].BufferCellType = BufferCellType.Leading;
                    }
                    else if ((characterBuffer[characterBufferIndex].Attributes & (ushort)NativeMethods.CHAR_INFO_Attributes.COMMON_LVB_TRAILING_BYTE)
                            == (ushort)NativeMethods.CHAR_INFO_Attributes.COMMON_LVB_TRAILING_BYTE)
                    {
                        contents[r, c].Character = (char)0;
                        contents[r, c].BufferCellType = BufferCellType.Trailing;
                    }
                    else
                    {
                        int charLength = LengthInBufferCells(contents[r, c].Character);
                        if (charLength == 2)
                        {
                            // When it's RasterFont, the "Leading_byte"/"Trailing_byte" are not effective, we
                            // need to decide the leading byte by checking the char length.
                            contents[r, c].BufferCellType = BufferCellType.Leading;
                            c++;
                            contents[r, c].Character = (char)0;
                            contents[r, c].ForegroundColor = fgColor;
                            contents[r, c].BackgroundColor = bgColor;
                            contents[r, c].BufferCellType = BufferCellType.Trailing;
                        }
                        else
                        {
                            contents[r, c].BufferCellType = BufferCellType.Complete;
                        }
                    }
                }
            }
            return true;
        }

        /// <summary>
        /// Can handle reading CJK characters, but the left and right edges are not checked
        /// </summary>
        /// <param name="consoleHandle"></param>
        /// <param name="codePage"></param>
        /// <param name="origin"></param>
        /// <param name="contentsRegion"></param>
        /// <param name="contents"></param>
        /// <exception cref="HostException">
        /// If there is not enough memory to complete calls to Win32's ReadConsoleOutput
        /// </exception>
        internal static void ReadConsoleOutputCJK
        (
            ConsoleHandle consoleHandle,
            uint codePage,
            Coordinates origin,
            Rectangle contentsRegion,
            ref BufferCell[,] contents
        )
        {
            int rows = contentsRegion.Bottom - contentsRegion.Top + 1;
            int cols = contentsRegion.Right - contentsRegion.Left + 1;

            if ((rows <= 0) || cols <= 0)
            {
                //tracer.WriteLine("invalid contents region");
                return;
            }

            int bufferLimit = 2 * 1024; // Limit is 8K bytes as each CHAR_INFO takes 4 bytes

            COORD bufferCoord;

            bufferCoord.X = 0;
            bufferCoord.Y = 0;

            // keeps track of which screen area is read
            SMALL_RECT readRegion;

            readRegion.Top = (short)origin.Y;

            int rowsRemaining = rows;

            while (rowsRemaining > 0)
            {
                // Iteration of columns is nested inside iteration of rows.
                // If the size of contents exceeds the buffer limit, reading is
                // done in blocks of size equal to the bufferlimit from left to right
                // then top to bottom.
                // For each iteration of rows,
                // - readRegion.Left and bufferSize.X are reset
                // - rowsRemaining, readRegion.Top, readRegion.Bottom, and bufferSize.Y
                //     are updated
                //   For each iteration of columns,
                //   - readRegion.Left, readRegion.Right and bufferSize.X are updated

                readRegion.Left = (short)origin.X;

                COORD bufferSize;
                bufferSize.X = (short)Math.Min(cols, bufferLimit);
                bufferSize.Y = (short)Math.Min
                                        (
                                            rowsRemaining,
                                            bufferLimit / bufferSize.X
                                        );
                readRegion.Bottom = (short)(readRegion.Top + bufferSize.Y - 1);

                // atContentsRow is at which row of contents a particular iteration is operating
                int atContentsRow = rows - rowsRemaining + contentsRegion.Top;

                // number of columns yet to be read
                int colsRemaining = cols;

                while (colsRemaining > 0)
                {
                    // atContentsCol is at which column of contents a particular iteration is operating
                    int atContentsCol = cols - colsRemaining + contentsRegion.Left;

                    readRegion.Right = (short)(readRegion.Left + bufferSize.X - 1);

                    // Now readRegion and bufferSize are updated.
                    Rectangle atContents = new Rectangle(atContentsCol, atContentsRow,
                                atContentsCol + bufferSize.X - 1, atContentsRow + bufferSize.Y - 1);
                    bool result =
                        ReadConsoleOutputCJKSmall(consoleHandle, codePage,
                            new Coordinates(readRegion.Left, readRegion.Top),
                            atContents,
                            ref contents);
                    if (result == false)
                    {
                        // When WriteConsoleOutput fails, half bufferLimit
                        if (bufferLimit < 2)
                        {
                            int err = Marshal.GetLastWin32Error();

                            HostException e = CreateHostException(err, "ReadConsoleOutput",
                                ErrorCategory.ReadError, "Win32 internal error \"{0}\" 0x{1:X} occurred while reading the console output buffer. Contact Microsoft Customer Support Services." ); //ConsoleControlStrings.ReadConsoleOutputExceptionTemplate);
                            throw e;
                        }
                        else
                        {
                            // if cols == colsRemaining, nothing is guaranteed read in this pass and
                            //  the unread area is still rectangular
                            bufferLimit /= 2;
                            if (cols == colsRemaining)
                            {
                                bufferSize.Y = 0;
                                break;
                            }
                            else
                            {
                                // some areas have been read. This could only happen when the number of columns
                                // to write is larger than bufferLimit. In that case, the algorithm reads one row
                                // at a time => bufferSize.Y == 1. Then, we can safely leave bufferSize.Y unchanged
                                // to retry with a smaller bufferSize.X.
                                Util.Assert(bufferSize.Y == 1, string.Format(CultureInfo.InvariantCulture, "bufferSize.Y should be 1, but is {0}", bufferSize.Y));
                                bufferSize.X = (short)Math.Min(colsRemaining, bufferLimit);
                                continue;
                            }
                        }
                    }
                    colsRemaining -= bufferSize.X;
                    readRegion.Left += bufferSize.X;
                    if (colsRemaining > 0 && (bufferSize.Y == 1) &&
                        (contents[atContents.Bottom, atContents.Right].Character == ' '))
                    {
                        colsRemaining++;
                        readRegion.Left--;
                    }
                    bufferSize.X = (short)Math.Min(colsRemaining, bufferLimit);
                }  // column iteration

                rowsRemaining -= bufferSize.Y;
                readRegion.Top += bufferSize.Y;
            }  // row iteration


            // The following nested loop set the value of the empty cells in contents:
            // character to ' '
            // foreground color to console's foreground color
            // background color to console's background color
            int rowIndex = contents.GetLowerBound(0);
            int rowEnd = contents.GetUpperBound(0);
            int colBegin = contents.GetLowerBound(1);
            int colEnd = contents.GetUpperBound(1);
            CONSOLE_SCREEN_BUFFER_INFO bufferInfo =
                        GetConsoleScreenBufferInfo(consoleHandle);
            ConsoleColor foreground = 0;
            ConsoleColor background = 0;

            WORDToColor(
                            bufferInfo.Attributes,
                            out foreground,
                            out background
                        );

            while (rowIndex <= rowEnd)
            {
                int colIndex = colBegin;
                while (true)
                {
                    // if contents[rowIndex,colIndex] is in contentsRegion, hence a non-empty cell,
                    // move colIndex to one past the right end of contentsRegion
                    if (contentsRegion.Top <= rowIndex && rowIndex <= contentsRegion.Bottom &&
                        contentsRegion.Left <= colIndex && colIndex <= contentsRegion.Right)
                    {
                        colIndex = contentsRegion.Right + 1;
                    }
                    // colIndex past contents last column
                    if (colIndex > colEnd)
                    {
                        break;
                    }
                    contents[rowIndex, colIndex] = new BufferCell(
                        ' ', foreground, background, BufferCellType.Complete);
                    colIndex++;
                }
                rowIndex++;
            }
        }
#endregion ReadConsoleOutput CJK


        private static void ReadConsoleOutputPlain
        (
            ConsoleHandle consoleHandle,
            Coordinates origin,
            Rectangle contentsRegion,
            ref BufferCell[,] contents
        )
        {
            int rows = contentsRegion.Bottom - contentsRegion.Top + 1;
            int cols = contentsRegion.Right - contentsRegion.Left + 1;

            if ((rows <= 0) || cols <= 0)
            {
                //tracer.WriteLine("invalid contents region");
                return;
            }

            int bufferLimit = 2 * 1024; // Limit is 8K bytes as each CHAR_INFO takes 4 bytes

            COORD bufferCoord;

            bufferCoord.X = 0;
            bufferCoord.Y = 0;

            // keeps track of which screen area read
            SMALL_RECT readRegion;

            readRegion.Top = (short)origin.Y;

            int rowsRemaining = rows;

            while (rowsRemaining > 0)
            {
                // Iteration of columns is nested inside iteration of rows.
                // If the size of contents exceeds the buffer limit, reading is
                // done in blocks of size equal to the bufferlimit from left to right
                // then top to bottom.
                // For each iteration of rows,
                // - readRegion.Left and bufferSize.X are reset
                // - rowsRemaining, readRegion.Top, readRegion.Bottom, and bufferSize.Y
                //     are updated
                //   For each iteration of columns,
                //   - readRegion.Left, readRegion.Right and bufferSize.X are updated

                readRegion.Left = (short)origin.X;

                COORD bufferSize;
                bufferSize.X = (short)Math.Min(cols, bufferLimit);
                bufferSize.Y = (short)Math.Min
                                        (
                                            rowsRemaining,
                                            bufferLimit / bufferSize.X
                                        );
                readRegion.Bottom = (short)(readRegion.Top + bufferSize.Y - 1);

                // atContentsRow is at which row of contents a particular iteration is operating
                int atContentsRow = rows - rowsRemaining + contentsRegion.Top;

                // number of columns yet to be read
                int colsRemaining = cols;

                while (colsRemaining > 0)
                {
                    readRegion.Right = (short)(readRegion.Left + bufferSize.X - 1);

                    // Now readRegion and bufferSize are updated.
                    // Call NativeMethods.ReadConsoleOutput
                    CHAR_INFO[] characterBuffer = new CHAR_INFO[bufferSize.Y * bufferSize.X];
                    bool result = NativeMethods.ReadConsoleOutput(
                                        consoleHandle.DangerousGetHandle(),
                                        characterBuffer,
                                        bufferSize,
                                        bufferCoord,
                                        ref readRegion);

                    if (result == false)
                    {
                        // When WriteConsoleOutput fails, half bufferLimit
                        if (bufferLimit < 2)
                        {
                            int err = Marshal.GetLastWin32Error();

                            HostException e = CreateHostException(err, "ReadConsoleOutput",
                                ErrorCategory.ReadError, "Win32 internal error \"{0}\" 0x{1:X} occurred while reading the console output buffer. Contact Microsoft Customer Support Services." ); //ConsoleControlStrings.ReadConsoleOutputExceptionTemplate);
                            throw e;
                        }
                        // if cols == colsRemaining, nothing is guaranteed read in this pass and
                        //  the unread area is still rectangular
                        bufferLimit /= 2;
                        if (cols == colsRemaining)
                        {
                            bufferSize.Y = 0;
                            break;
                        }
                        else
                        {
                            // some areas have been read. This could only happen when the number of columns
                            // to write is larger than bufferLimit. In that case, the algorithm reads one row
                            // at a time => bufferSize.Y == 1. Then, we can safely leave bufferSize.Y unchanged
                            // to retry with a smaller bufferSize.X.
                            Util.Assert(bufferSize.Y == 1, string.Format(CultureInfo.InvariantCulture, "bufferSize.Y should be 1, but is {0}", bufferSize.Y));
                            bufferSize.X = (short)Math.Min(colsRemaining, bufferLimit);
                            continue;
                        }
                    }

                    // atContentsCol is at which column of contents a particular iteration is operating
                    int atContentsCol = cols - colsRemaining + contentsRegion.Left;

                    // copy characterBuffer to contents;
                    int characterBufferIndex = 0;
                    for (int r = atContentsRow; r < bufferSize.Y + atContentsRow; r++)
                    {
                        for (int c = atContentsCol; c < bufferSize.X + atContentsCol; c++, characterBufferIndex++)
                        {
                            contents[r, c].Character = (char)
                                characterBuffer[characterBufferIndex].UnicodeChar;
                            ConsoleColor fgColor, bgColor;
                            WORDToColor(characterBuffer[characterBufferIndex].Attributes,
                                out fgColor,
                                out bgColor);
                            contents[r, c].ForegroundColor = fgColor;
                            contents[r, c].BackgroundColor = bgColor;
                        }
                    }
                    colsRemaining -= bufferSize.X;
                    readRegion.Left += bufferSize.X;
                    bufferSize.X = (short)Math.Min(colsRemaining, bufferLimit);
                }  // column iteration

                rowsRemaining -= bufferSize.Y;
                readRegion.Top += bufferSize.Y;
            }  // row iteration

            // The following nested loop set the value of the empty cells in contents:
            // character to ' '
            // foreground color to console's foreground color
            // background color to console's background color
            int rowIndex = contents.GetLowerBound(0);
            int rowEnd = contents.GetUpperBound(0);
            int colBegin = contents.GetLowerBound(1);
            int colEnd = contents.GetUpperBound(1);
            CONSOLE_SCREEN_BUFFER_INFO bufferInfo =
                        GetConsoleScreenBufferInfo(consoleHandle);
            ConsoleColor foreground = 0;
            ConsoleColor background = 0;

            WORDToColor(
                            bufferInfo.Attributes,
                            out foreground,
                            out background
                       );

            while (rowIndex <= rowEnd)
            {
                int colIndex = colBegin;
                while (true)
                {
                    // if contents[rowIndex,colIndex] is in contentsRegion, hence a non-empty cell,
                    // move colIndex to one past the right end of contentsRegion
                    if (contentsRegion.Top <= rowIndex && rowIndex <= contentsRegion.Bottom &&
                        contentsRegion.Left <= colIndex && colIndex <= contentsRegion.Right)
                    {
                        colIndex = contentsRegion.Right + 1;
                    }
                    // colIndex past contents last column
                    if (colIndex > colEnd)
                    {
                        break;
                    }
                    contents[rowIndex, colIndex].Character = ' ';
                    contents[rowIndex, colIndex].ForegroundColor = foreground;
                    contents[rowIndex, colIndex].BackgroundColor = background;
                    colIndex++;
                }
                rowIndex++;
            }
        }


        /// <summary>
        /// Wraps Win32 FillConsoleOutputCharacter
        /// </summary>
        /// <param name="consoleHandle">
        /// 
        /// handle for the console where output is filled
        /// 
        /// </param>
        /// <param name="character">
        /// 
        /// character to fill the console output
        /// 
        /// </param>
        /// <param name="numberToWrite">
        /// 
        /// number of times to write character
        /// 
        /// </param>
        /// <param name="origin">
        /// 
        /// location on screen buffer where writing starts
        /// 
        /// </param>
        /// <exception cref="HostException">
        /// If Win32's FillConsoleOutputCharacter fails
        /// </exception>
        internal static void FillConsoleOutputCharacter
        (
            ConsoleHandle consoleHandle,
            char character,
            int numberToWrite,
            Coordinates origin
        )
        {
            Util.Assert(!consoleHandle.IsInvalid, "ConsoleHandle is not valid");
            Util.Assert(!consoleHandle.IsClosed, "ConsoleHandle is closed");

            COORD c;

            c.X = (short)origin.X;
            c.Y = (short)origin.Y;

            DWORD unused = 0;
            bool result =
                NativeMethods.FillConsoleOutputCharacter(
                    consoleHandle.DangerousGetHandle(),
                    character,
                    (DWORD)numberToWrite,
                    c,
                    out unused);
            if (result == false)
            {
                int err = Marshal.GetLastWin32Error();

                HostException e = CreateHostException(err, "FillConsoleOutputCharacter",
                    ErrorCategory.WriteError, "The Win32 internal error \"{0}\" 0x{1:X} occurred while filling the console output buffer with characters. Contact Microsoft Customer Support Services." ); //ConsoleControlStrings.FillConsoleOutputCharacterExceptionTemplate);
                throw e;
            }
            // we don't assert that the number actually written matches the number we asked for, as the function may clip if
            // the number of cells to write extends past the end of the screen buffer.
        }

        /// <summary>
        /// Wraps Win32 FillConsoleOutputAttribute
        /// </summary>
        /// <param name="consoleHandle">
        /// 
        /// handle for the console where output is filled
        /// 
        /// </param>
        /// <param name="attribute">
        /// 
        /// attribute to fill the console output
        /// 
        /// </param>
        /// <param name="numberToWrite">
        /// 
        /// number of times to write attribute
        /// 
        /// </param>
        /// <param name="origin">
        /// 
        /// location on screen buffer where writing starts
        /// 
        /// </param>
        /// <exception cref="HostException">
        /// If Win32's FillConsoleOutputAttribute fails
        /// </exception>
        internal static void FillConsoleOutputAttribute
        (
            ConsoleHandle consoleHandle,
            WORD attribute,
            int numberToWrite,
            Coordinates origin
        )
        {
            Util.Assert(!consoleHandle.IsInvalid, "ConsoleHandle is not valid");
            Util.Assert(!consoleHandle.IsClosed, "ConsoleHandle is closed");

            COORD c;

            c.X = (short)origin.X;
            c.Y = (short)origin.Y;

            DWORD unused = 0;
            bool result =
                NativeMethods.FillConsoleOutputAttribute(
                    consoleHandle.DangerousGetHandle(),
                    attribute,
                    (DWORD)numberToWrite,
                    c,
                    out unused);

            if (result == false)
            {
                int err = Marshal.GetLastWin32Error();

                HostException e = CreateHostException(err, "FillConsoleOutputAttribute",
                    ErrorCategory.WriteError, "The Win32 internal error \"{0}\" 0x{1:X} occurred while filling the console output buffer with attributes. Contact Microsoft Customer Support Services." ); //ConsoleControlStrings.FillConsoleOutputAttributeExceptionTemplate);
                throw e;
            }
        }

        /// <summary>
        /// Wrap Win32 ScrollConsoleScreenBuffer
        /// </summary>
        /// <param name="consoleHandle">
        /// 
        /// handle for the console where screen buffer is scrolled
        /// 
        /// </param>
        /// <param name="scrollRectangle">
        /// 
        /// area to be scrolled
        /// 
        /// </param>
        /// <param name="clipRectangle">
        /// 
        /// area to be updated after scrolling
        /// 
        /// </param>
        /// <param name="destOrigin">
        /// 
        /// location to which the top left corner of scrollRectangle move
        /// 
        /// </param>
        /// <param name="fill">
        /// 
        /// character and attribute to fill the area vacated by the scroll
        /// 
        /// </param>
        /// <exception cref="HostException">
        /// If Win32's ScrollConsoleScreenBuffer fails
        /// </exception>

        internal static void ScrollConsoleScreenBuffer
        (
            ConsoleHandle consoleHandle,
            SMALL_RECT scrollRectangle,
            SMALL_RECT clipRectangle,
            COORD destOrigin, CHAR_INFO fill
        )
        {
            Util.Assert(!consoleHandle.IsInvalid, "ConsoleHandle is not valid");
            Util.Assert(!consoleHandle.IsClosed, "ConsoleHandle is closed");

            bool result =
                NativeMethods.ScrollConsoleScreenBuffer(
                    consoleHandle.DangerousGetHandle(),
                    ref scrollRectangle,
                    ref clipRectangle,
                    destOrigin,
                    ref fill);

            if (result == false)
            {
                int err = Marshal.GetLastWin32Error();

                HostException e = CreateHostException(err, "ScrollConsoleScreenBuffer",
                    ErrorCategory.WriteError, "The Win32 internal error \"{0}\" 0x{1:X} occurred while scrolling the console output buffer. Contact Microsoft Customer Support Services." ); //ConsoleControlStrings.ScrollConsoleScreenBufferExceptionTemplate);
                throw e;
            }
        }

#endregion Buffer

#region Window

        /// <summary>
        /// Wraps Win32 SetConsoleWindowInfo
        /// </summary>
        /// <param name="consoleHandle">
        /// 
        /// handle for the console where window info is set
        /// 
        /// </param>
        /// <param name="absolute">
        /// 
        /// If this parameter is TRUE, the coordinates specify the new upper-left and
        /// lower-right corners of the window. If it is false, the coordinates are offsets
        /// to the current window-corner coordinates
        /// 
        /// </param>
        /// <param name="windowInfo">
        /// 
        /// specify the size and position of the console screen buffer's window
        /// 
        /// </param>
        /// <exception cref="HostException">
        /// If Win32's SetConsoleWindowInfo fails
        /// </exception>

        internal static void SetConsoleWindowInfo(ConsoleHandle consoleHandle, bool absolute, SMALL_RECT windowInfo)
        {
            Util.Assert(!consoleHandle.IsInvalid, "ConsoleHandle is not valid");
            Util.Assert(!consoleHandle.IsClosed, "ConsoleHandle is closed");

            bool result = NativeMethods.SetConsoleWindowInfo(consoleHandle.DangerousGetHandle(), absolute, ref windowInfo);

            if (result == false)
            {
                int err = Marshal.GetLastWin32Error();

                HostException e = CreateHostException(err, "SetConsoleWindowInfo",
                    ErrorCategory.ResourceUnavailable, "The Win32 internal error \"{0}\" 0x{1:X} occurred while setting the console window information. Contact Microsoft Customer Support Services." ); //ConsoleControlStrings.SetConsoleWindowInfoExceptionTemplate);
                throw e;
            }
        }

        /// <summary>
        /// Wraps Win32 GetLargestConsoleWindowSize
        /// </summary>
        /// <param name="consoleHandle">
        /// 
        /// handle for the console for which the largest window size is obtained
        /// 
        /// </param>
        /// <returns>
        /// 
        /// the largest window size
        /// 
        /// </returns>
        /// <exception cref="HostException">
        /// If Win32's GetLargestConsoleWindowSize fails
        /// </exception>

        internal static Size GetLargestConsoleWindowSize(ConsoleHandle consoleHandle)
        {
            Util.Assert(!consoleHandle.IsInvalid, "ConsoleHandle is not valid");
            Util.Assert(!consoleHandle.IsClosed, "ConsoleHandle is closed");

            COORD result = NativeMethods.GetLargestConsoleWindowSize(consoleHandle.DangerousGetHandle());

            if ((result.X == 0) && (result.Y == 0))
            {
                int err = Marshal.GetLastWin32Error();

                HostException e = CreateHostException(err, "GetLargestConsoleWindowSize",
                    ErrorCategory.ResourceUnavailable, "The Win32 internal error \"{0}\" 0x{1:X} occurred while getting the largest console window size. Contact Microsoft Customer Support Services." ); //ConsoleControlStrings.GetLargestConsoleWindowSizeExceptionTemplate);
                throw e;
            }

            return new Size(result.X, result.Y);
        }



        /// <summary>
        /// Wraps Win32 GetConsoleTitle. 1K is the safe limit experimentally. The 64K limit
        /// found in the docs is disregarded because it is essentially meaningless.
        /// </summary>
        /// <returns>
        /// 
        /// a string for the title of the window
        /// 
        /// </returns>
        /// <exception cref="HostException">
        /// If Win32's GetConsoleTitle fails
        /// </exception>

        internal static string GetConsoleWindowTitle()
        {
            const int MaxWindowTitleLength = 1024;
            DWORD bufferSize = MaxWindowTitleLength;
            DWORD result;
            StringBuilder consoleTitle = new StringBuilder((int)bufferSize);

            // Suppress the PreFAST warning about not using Marshal.GetLastWin32Error() to
            // get the error code.
#pragma warning disable 56523
            result = NativeMethods.GetConsoleTitle(consoleTitle, bufferSize);
            // If the result is zero, it may mean and error but it may also mean
            // that the window title has been set to null. Since we can't tell the
            // the difference, we'll just return the empty string every time.
            if (result == 0)
            {
                return String.Empty;
            }

            return consoleTitle.ToString();
        }

        /// <summary>
        /// Wraps Win32 SetConsoleTitle
        /// </summary>
        /// <param name="consoleTitle">
        /// 
        /// a string for the title of the window
        /// 
        /// </param>
        /// <exception cref="HostException">
        /// If Win32's SetConsoleTitle fails
        /// </exception>

        internal static void SetConsoleWindowTitle(string consoleTitle)
        {
            bool result = NativeMethods.SetConsoleTitle(consoleTitle);

            if (result == false)
            {
                int err = Marshal.GetLastWin32Error();

                HostException e = CreateHostException(err, "SetConsoleWindowTitle",
                    ErrorCategory.ResourceUnavailable, "The Win32 internal error \"{0}\" 0x{1:X} occurred while setting the console window title. Contact Microsoft Customer Support Services." ); //ConsoleControlStrings.SetConsoleWindowTitleExceptionTemplate);
                throw e;
            }
        }

#endregion Window

#region Font Selection

        /// <summary>
        /// UpdateLocaleSpecificFont is a helper method used to update 
        /// the console font based on the locale.
        /// The default font face used for Powershell Console is Lucida Console.
        /// However certain locales dont support Lucida Console font. Hence for such
        /// locales the console font is updated to Raster dynamically.
        /// </summary>
        internal static void UpdateLocaleSpecificFont()
        {
            // Default Powershell shortcut.lnk settings. 
            const string defaultFontFace = "Lucida Console";

            // Default CJK locale shortcut.lnk settings.
            // Font size is hard coded here to ensure we select a supported size.
            // GDI does a poor job of selecting raster font size if the requested 
            // size is not supported.
            const string CJKFontFace = "Terminal";
            const int CJKFontFamily = 48;
            const int CJKnFont = 6;
            const int CJKFontWidth = 8;
            const int CJKFontHeight = 12;
            const int CKJFontWeight = 400;

            uint currentLocaleCodePage = (uint)ConsoleControl.NativeMethods.GetConsoleCP();

            ConsoleHandle handle = ConsoleControl.GetActiveScreenBufferHandle();
            CONSOLE_FONT_INFO_EX fontInfo;
            try
            {
                fontInfo = ConsoleControl.GetConsoleFontInfo(handle);
            }
            catch (Exception)
            {
                return;
            }

            bool isLucidaConsoleSupportedLocale = CodePageSupportsLucida(currentLocaleCodePage);

            // The Raster font is updated for Japanese and Korean locales if the user has not manually
            // altered the default settings. If the default settings are altered then the
            // setting chosen by the user would be used.
            if (!isLucidaConsoleSupportedLocale &&
                fontInfo.FontFace.Equals(defaultFontFace, StringComparison.OrdinalIgnoreCase))
            {
                fontInfo.FontFace = CJKFontFace;
                fontInfo.FontFamily = CJKFontFamily;
                fontInfo.nFont = CJKnFont;
                fontInfo.FontWidth = CJKFontWidth;
                fontInfo.FontHeight = CJKFontHeight;
                fontInfo.FontWeight = CKJFontWeight;

                bool result = NativeMethods.SetCurrentConsoleFontEx(handle.DangerousGetHandle(), false, ref fontInfo);

                if (result == false)
                {
                    int err = Marshal.GetLastWin32Error();

                    HostException e = CreateHostException(err,
                        "SetConsoleFontInfo",
                        ErrorCategory.ResourceUnavailable,
                        "The Win32 internal error \"{0}\" 0x{1:X} occurred while setting console font information. Contact Microsoft Customer Support Services."); // ConsoleControlStrings.SetConsoleFontInfoExceptionTemplate

                    throw e;
                }
            }
        }

        /// <summary>
        /// Supported Lucida code pages obtained from Font group.
        /// Contacts: alib, judysa, simonda
        /// </summary>
        private static HashSet<uint> LucidaSupportedCodePages = new HashSet<uint>()
        {
            1251,               // Latin 1
            1250,               // Latin 2
            1251,               // Cyrillic
            1253,               // Greek
            1254,               // Turkish
            869,                // IBM Greek
            866,                // MS-DOS Russian
            865,                // MS-DOS Nordic
            863,                // MS-DOS Canadian French
            861,                // MS-DOS Icelandic
            860,                // MS-DOS Portuguese
            857,                // IBM Turkish
            855,                // IBM Cyrillic
            852,                // Latin 2
            737,                // Greek
            850,                // WE/Latin 1
            437                 // US
            //936,                // SimplifiedChinese -  According to font team this is *not* supported.
            //950                 // TraditionalChinese - According to font team this is *not* supported.
        };
        private static bool CodePageSupportsLucida(uint currentLocaleCodePage)
        {
            return LucidaSupportedCodePages.Contains(currentLocaleCodePage);
        }

        #endregion

        /// <summary>
        /// Wrap Win32 WriteConsole
        /// </summary>
        /// <param name="consoleHandle">
        /// handle for the console where the string is written
        /// </param>
        /// <param name="output">
        /// string that is written
        /// </param>
        /// <param name="newLine">
        /// New line is written.
        /// </param>
        /// <exception cref="HostException">
        /// if the Win32's WriteConsole fails 
        /// </exception>
        internal static void WriteConsole( ConsoleHandle consoleHandle, ReadOnlySpan< char > output, bool newLine = false )
        {
            Util.Assert( !consoleHandle.IsInvalid, "ConsoleHandle is not valid" );
            Util.Assert( !consoleHandle.IsClosed, "ConsoleHandle is closed" );

            if( output.IsEmpty )
            {
                if( newLine )
                {
                    _RealWriteConsole( consoleHandle, Environment.NewLine.AsSpan() );
                }
                return;
            }


            // [danthom] Debugging note: put a breakpoint here to catch output as it
            // is going out.

            // If a newline gets injected between strings where a color control
            // sequence is broken across them, things blow up terribly.
            if( ColorHostUserInterface.Crlf.AsSpan().SequenceEqual( output ) )
            {
                _RealWriteConsole( consoleHandle, output );
            }
            else
            {
                sm_colorWriter.Write( consoleHandle, output, newLine );
            }

        }// end WriteConsole()

        // A problem with doing this at such an incredibly low level is that if there is any
        // kind of problem with it, and then we try to WriteErrorLine... and hilarity ensues.
        // TODO: a possible mitigation is if we detect an invalid control code or somesuch,
        // reset the colors and parse state.
        private static readonly AnsiColorWriter sm_colorWriter = new AnsiColorWriter();

        private static void _RealWriteConsole( ConsoleHandle handle, ReadOnlySpan< char > s )
        {
            bool itWorked = NativeMethods.WriteConsole( handle,
                                                        in MemoryMarshal.GetReference( s ),
                                                        (DWORD) s.Length,
                                                        out uint _,
                                                        IntPtr.Zero );
            if( !itWorked )
            {
                int err = Marshal.GetLastWin32Error();

                HostException e = CreateHostException( err, "WriteConsole",
                    ErrorCategory.WriteError, "The Win32 internal error \"{0}\" 0x{1:X} occurred while writing to the console output buffer at the current cursor position. Contact Microsoft Customer Support Services." ); //ConsoleControlStrings.WriteConsoleExceptionTemplate);
                throw e;
            }
        } // end _RealWriteConsole()


        /// <summary>
        /// Wraps Win32 SetConsoleTextAttribute
        /// </summary>
        /// <param name="consoleHandle">
        /// 
        /// handle for the console where text attribute is set
        /// 
        /// </param>
        /// <param name="attribute">
        /// 
        /// text attribute to set the console
        /// 
        /// </param>
        /// <exception cref="HostException">
        /// 
        /// if the Win32's SetConsoleTextAttribute fails 
        /// 
        /// </exception>

        internal static void SetConsoleTextAttribute(ConsoleHandle consoleHandle, WORD attribute)
        {
            Util.Assert(!consoleHandle.IsInvalid, "ConsoleHandle is not valid");
            Util.Assert(!consoleHandle.IsClosed, "ConsoleHandle is closed");

            bool result = NativeMethods.SetConsoleTextAttribute(consoleHandle.DangerousGetHandle(), attribute);

            if (result == false)
            {
                int err = Marshal.GetLastWin32Error();

                HostException e = CreateHostException(err, "SetConsoleTextAttribute",
                    ErrorCategory.ResourceUnavailable, "The Win32 internal error \"{0}\" 0x{1:X} occurred while setting character attributes for the console output buffer. Contact Microsoft Customer Support Services." ); //ConsoleControlStrings.SetConsoleTextAttributeExceptionTemplate);
                throw e;
            }
        }


#region Dealing with CJK

        /// <summary>
        /// From IsConsoleFullWidth in \windows\core\ntcon\server\dbcs.c
        /// Gets the CharSet for a code page
        /// </summary>
        /// <param name="codePage"></param>
        /// <returns>The CharSet corresponding to the codePage; defaults to OEM_CHARSET (255)</returns>
        private static uint CodePageToCharSet(uint codePage)
        {
            CHARSETINFO csi;
            const DWORD TCI_SRCCODEPAGE = 2;
            const uint OEM_CHARSET = 255;
            // Suppress the PreFAST warning about not using Marshal.GetLastWin32Error() to
            // get the error code.
#pragma warning disable 56523
            if (!NativeMethods.TranslateCharsetInfo((IntPtr)codePage, out csi, TCI_SRCCODEPAGE))
            {
                csi.ciCharset = OEM_CHARSET;
            }
            return csi.ciCharset;
        }

        // From \windows\core\ntcon\server\dbcs.c
        private static bool IsAvailableFarEastCodePage(uint codePage)
        {
            uint charSet = CodePageToCharSet(codePage);
            return IsAnyDBCSCharSet(charSet);
        }

        // From \windows\core\ntcon\server\dbcs.c
        private static bool IsAnyDBCSCharSet(uint charSet)
        {
            const uint SHIFTJIS_CHARSET = 128;
            const uint HANGUL_CHARSET = 129;
            const uint CHINESEBIG5_CHARSET = 136;
            const uint GB2312_CHARSET = 134;
            return charSet == SHIFTJIS_CHARSET || charSet == HANGUL_CHARSET ||
                charSet == CHINESEBIG5_CHARSET || charSet == GB2312_CHARSET;
        }

        /// <summary>
        /// From IsConsoleFullWidth in \windows\core\ntcon\server\dbcs.c
        /// Precondition: the current code page needs to be a Far East code page.
        /// 
        /// char F8F8 makes this function return 1 while in CHT, CHS, and KOR it takes 2 cells.
        /// I don't think we should special-case this because that ought to be a bug outside of
        /// this code.
        /// </summary>
        /// <param name="c"></param>
        /// <param name="hwnd">window handle</param>
        /// <param name="hDC">handle to DC; it is not released by this method</param>
        /// <param name="istmInitialized"></param>
        /// <param name="tm"></param>
        /// <returns></returns>
        private static int LengthInBufferCellsFE(char c, ref HWND hwnd, ref HDC hDC, ref bool istmInitialized, ref TEXTMETRIC tm)
        {
            if (0x20 <= c && c <= 0x7e)
            {
                /* ASCII */
                return 1;
            }
            else if (0x3041 <= c && c <= 0x3094)
            {
                /* Hiragana */
                return 2;
            }
            else if (0x30a1 <= c && c <= 0x30f6)
            {
                /* Katakana */
                return 2;
            }
            else if (0x3105 <= c && c <= 0x312c)
            {
                /* Bopomofo */
                return 2;
            }
            else if (0x3131 <= c && c <= 0x318e)
            {
                /* Hangul Elements */
                return 2;
            }
            else if (0xac00 <= c && c <= 0xd7a3)
            {
                /* Korean Hangul Syllables */
                return 2;
            }
            else if (0xff01 <= c && c <= 0xff5e)
            {
                /* Fullwidth ASCII variants */
                return 2;
            }
            else if (0xff61 <= c && c <= 0xff9f)
            {
                /* Halfwidth Katakana variants */
                return 1;
            }
            else if ((0xffa0 <= c && c <= 0xffbe) ||
                     (0xffc2 <= c && c <= 0xffc7) ||
                     (0xffca <= c && c <= 0xffcf) ||
                     (0xffd2 <= c && c <= 0xffd7) ||
                     (0xffda <= c && c <= 0xffdc))
            {
                /* Halfwidth Hangul variants */
                return 1;
            }
            else if (0xffe0 <= c && c <= 0xffe6)
            {
                /* Fullwidth symbol variants */
                return 2;
            }
            else if (0x4e00 <= c && c <= 0x9fa5)
            {
                /* Han Ideographic */
                return 2;
            }
            else if (0xf900 <= c && c <= 0xfa2d)
            {
                /* Han Compatibility Ideographs */
                return 2;
            }
            else
            {
                /* Unknown character: need to use GDI*/
                if (hDC == (IntPtr)0)
                {
                    hwnd = NativeMethods.GetConsoleWindow();
                    if ((IntPtr)0 == hwnd)
                    {
                        int err = Marshal.GetLastWin32Error();
                        //Don't throw exception so that output can continue
                        //tracer.TraceError("Win32 Error 0x{0:X} occurred when getting the window handle to the console.",
                            //err);
                        return 1;
                    }
                    hDC = NativeMethods.GetDC(hwnd);
                    if ((IntPtr)0 == hDC)
                    {
                        int err = Marshal.GetLastWin32Error();
                        //Don't throw exception so that output can continue
                        //tracer.TraceError("Win32 Error 0x{0:X} occurred when getting the Device Context of the console window.",
                            //err);
                        return 1;
                    }
                }
                bool result = true;
                if (!istmInitialized)
                {
                    result = NativeMethods.GetTextMetrics(hDC, out tm);
                    if (!result)
                    {
                        int err = Marshal.GetLastWin32Error();
                        //Don't throw exception so that output can continue
                        //tracer.TraceError("Win32 Error 0x{0:X} occurred when getting the Text Metric of the console window's Device Context.",
                            //err);
                        return 1;
                    }
                    istmInitialized = true;
                }
                int width;
                result = NativeMethods.GetCharWidth32(hDC, (uint)c, (uint)c, out width);
                if (!result)
                {
                    int err = Marshal.GetLastWin32Error();
                    //Don't throw exception so that output can continue
                    //tracer.TraceError("Win32 Error 0x{0:X} occurred when getting the width of a char.",
                        //err);
                    return 1;
                }
                if (width >= tm.tmMaxCharWidth)
                {
                    return 2;
                }
            }
            //tracer.WriteLine("failed to locate char {0}, return 1", (int)c);
            return 1;
        }

        internal static int LengthInBufferCells(char c)
        {
            uint codePage = NativeMethods.GetConsoleOutputCP();
            return LengthInBufferCells(c, codePage);
        }

        /// <summary>
        /// From IsConsoleFullWidth in \windows\core\ntcon\server\dbcs.c
        /// </summary>
        /// <param name="c"></param>
        /// <param name="codePage"></param>
        /// <returns></returns>
        [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults",
            MessageId = "Microsoft.PowerShell.ConsoleControl+NativeMethods.ReleaseDC(System.IntPtr,System.IntPtr)")]
        private static int LengthInBufferCells(char c, uint codePage)
        {
            if (!IsAvailableFarEastCodePage(codePage))
            {
                return 1;
            }
            HWND hwnd = (HWND)0;
            HDC hDC = (HDC)0;
            bool istmInitialized = false;
            TEXTMETRIC tm = new TEXTMETRIC(); ;
            try
            {
                return LengthInBufferCellsFE(c, ref hwnd, ref hDC, ref istmInitialized, ref tm);
            }
            finally
            {
                if (hwnd != (IntPtr)0 && hDC != (IntPtr)0)
                {
                    NativeMethods.ReleaseDC(hwnd, hDC);
                }
            }
        }


        // Return the length of a VT100 control sequence character in str starting
        // at the given offset.
        //
        // This code only handles the most common formatting sequences, which are
        // all of the pattern:
        //     ESC '[' digits+ (';' digits)* 'm'
        // 
        // There are many other VT100 escape sequences, but this simple pattern
        // is sufficient for our formatting system.  We won't handle cursor movements
        // or other attempts at animation.
        //
        // Note that offset is adjusted past the escape sequence.
        internal static int ControlSequenceLength(string str, ref int offset)
        {
            var start = offset;
            if (str[offset++] != (char)0x1B)
                return 0;

            if (offset >= str.Length || str[offset] != '[')
                return 0;

            offset += 1;
            while (offset < str.Length)
            {
                var c = str[offset++];
                if (c == 'm')
                    break;

                if (char.IsDigit(c) || c == ';')
                    continue;

                return 0;
            }

            return offset - start;
        }

        // TODO: pick up VT100-related changes from PowerShell
        /// <summary>
        /// From IsConsoleFullWidth in \windows\core\ntcon\server\dbcs.c
        /// </summary>
        /// <returns></returns>
        [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults",
            MessageId = "Microsoft.PowerShell.ConsoleControl+NativeMethods.ReleaseDC(System.IntPtr,System.IntPtr)")]
        internal static int LengthInBufferCells(string str, int offset)
        {
            // [danthom]
            // We have a problem here. The problem is that 'str' might have color sequences in it,
            // which mess up length calculations. A partial mitigation is to strip out the color
            // sequences here when measuring the length. But that doesn't fix all the problems--even
            // though we will then get an accurate result from LengthInBufferCells, the built-in
            // Out-Default will use that answer to chop a string if it wraps, but the built-in
            // Out-Default is /not/ color-aware (so it will end up cutting in some spot much earlier
            // than intended). I'm not aware of any way around this, except to not use the built-in
            // Out-Default. :( :( :(
            //
            // Here is a sample stack of where this gets called (and the answer is subsequently used to chop the string if necessary):
            //    > DbgShell.exe!MS.DbgShell.ConsoleControl.LengthInBufferCells(string str, int offset) Line 3057   C#
            //      DbgShell.exe!MS.DbgShell.ColorHostRawUserInterface.LengthInBufferCells(string s, int offset) Line 1306 + 0xe bytes  C#
            //      DbgShell.exe!MS.DbgShell.ColorHostRawUserInterface.LengthInBufferCells(string s) Line 1284 + 0x1c bytes C#
            //      System.Management.Automation.dll!Microsoft.PowerShell.Commands.Internal.Format.DisplayCellsPSHost.Length(string str) + 0x28 bytes
            //      System.Management.Automation.dll!Microsoft.PowerShell.Commands.Internal.Format.WriteLineHelper.WriteLineInternal(string val, int cols) + 0xa4 bytes
            //      System.Management.Automation.dll!Microsoft.PowerShell.Commands.Internal.Format.ConsoleLineOutput.WriteLine(string s) + 0x31 bytes
            //      System.Management.Automation.dll!Microsoft.PowerShell.Commands.Internal.Format.OutCommandInner.ProcessPayload(Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData fed, Microsoft.PowerShell.Commands.Internal.Format.FormatMessagesContextManager.OutputContext c) + 0x1c1 bytes
            //      System.Management.Automation.dll!Microsoft.PowerShell.Commands.Internal.Format.OutCommandInner.ProcessObject(System.Management.Automation.PSObject so) + 0x1a7 bytes
            //      System.Management.Automation.dll!Microsoft.PowerShell.Commands.Internal.Format.OutCommandInner.ProcessRecord() + 0xda bytes
            //      System.Management.Automation.dll!System.Management.Automation.CommandProcessor.ProcessRecord() + 0x27a bytes
            //      System.Management.Automation.dll!System.Management.Automation.CommandProcessorBase.DoExecute() + 0xe5 bytes
            //      System.Management.Automation.dll!System.Management.Automation.Internal.PipelineProcessor.DoStepItems(object input, System.Collections.Hashtable errorResults, bool enumerate) + 0x6e bytes
            //      System.Management.Automation.dll!Microsoft.PowerShell.Commands.Internal.Format.CommandWrapper.Process(object o) + 0x35 bytes
            //      System.Management.Automation.dll!System.Management.Automation.CommandProcessor.ProcessRecord() + 0x27a bytes
            //      System.Management.Automation.dll!System.Management.Automation.CommandProcessorBase.DoExecute() + 0xe5 bytes
            //      mscorlib.dll!System.Security.SecurityContext.Run(System.Security.SecurityContext securityContext, System.Threading.ContextCallback callback, object state) + 0x96 bytes
            //      System.Management.Automation.dll!System.Management.Automation.MshCommandRuntime.WriteObject(object sendToPipeline) + 0x9e bytes
            //      System.Management.Automation.dll!System.Management.Automation.Cmdlet.WriteObject(object sendToPipeline) + 0xb2 bytes
            //      DbgProvider.dll!MS.Dbg.Formatting.Commands.FormatAltCustomCommand.ApplyViewToInputObject() Line 19 + 0xf bytes  C#
            //      ...
            //

            var orig = str;
            str = CaStringUtil.StripControlSequences( str );
            if( str.Length != orig.Length )
            {
                // If there are color sequences involved, I'll hope that my formatters are
                // also involved, and we'll just outright lie--we'll let the console wrap
                // us.
                return 5;
            }
            Util.Assert(offset >= 0, "offset >= 0");
            Util.Assert(string.IsNullOrEmpty(str) || (offset < str.Length), "offset < str.Length");

            uint codePage = NativeMethods.GetConsoleOutputCP();
            if (!IsAvailableFarEastCodePage(codePage))
            {
                return str.Length - offset;
            }

            HWND hwnd = (HWND)0;
            HDC hDC = (HDC)0;
            bool istmInitialized = false;
            TEXTMETRIC tm = new TEXTMETRIC(); ;
            int length = 0;
            try
            {
                int n = str.Length;
                for (int i = offset; i < n; i++)
                {
                    char c = str[i];
                    length += LengthInBufferCellsFE(c, ref hwnd, ref hDC, ref istmInitialized, ref tm);
                }
                return length;
            }
            finally
            {
                if (hwnd != (IntPtr)0 && hDC != (IntPtr)0)
                {
                    NativeMethods.ReleaseDC(hwnd, hDC);
                }
            }
        }


        /// <summary>
        /// Check if the output buffer code page is Japanese, Simplified Chinese, Korean, or Traditional Chinese
        /// </summary>
        /// <param name="codePage"></param>
        /// <returns></returns>
        internal static bool IsCJKOutputCodePage(out uint codePage)
        {
            codePage = NativeMethods.GetConsoleOutputCP();
            return codePage == 932 || // Japanese
                codePage == 936 || // Simplified Chinese
                codePage == 949 || // Korean
                codePage == 950;  // Traditional Chinese
        }

#endregion Dealing with CJK


#region Cursor


        /// <summary>
        /// Wraps Win32 SetConsoleCursorPosition
        /// </summary>
        /// <param name="consoleHandle">
        /// 
        /// handle for the console where cursor position is set
        ///
        /// </param>
        /// <param name="cursorPosition">
        /// 
        /// location to which the cursor will be set
        /// 
        /// </param>
        /// <exception cref="HostException">
        /// If Win32's SetConsoleCursorPosition fails
        /// </exception>

        internal static void SetConsoleCursorPosition(ConsoleHandle consoleHandle, Coordinates cursorPosition)
        {
            Util.Assert(!consoleHandle.IsInvalid, "ConsoleHandle is not valid");
            Util.Assert(!consoleHandle.IsClosed, "ConsoleHandle is closed");

            ConsoleControl.COORD c;

            c.X = (short)cursorPosition.X;
            c.Y = (short)cursorPosition.Y;

            bool result = NativeMethods.SetConsoleCursorPosition(consoleHandle.DangerousGetHandle(), c);

            if (result == false)
            {
                int err = Marshal.GetLastWin32Error();

                HostException e = CreateHostException(err, "SetConsoleCursorPosition",
                    ErrorCategory.ResourceUnavailable, "The Win32 internal error \"{0}\" 0x{1:X} occurred while trying to set the cursor position. Contact Microsoft Customer Support Services." ); //ConsoleControlStrings.SetConsoleCursorPositionExceptionTemplate);
                throw e;
            }
        }



        /// <summary>
        /// Wraps Win32 GetConsoleCursorInfo
        /// </summary>
        /// <param name="consoleHandle">
        /// 
        /// handle for the console where cursor info is obtained
        /// 
        /// </param>
        /// <returns>
        /// 
        /// cursor info
        /// 
        /// </returns>
        /// <exception cref="HostException">
        /// If Win32's GetConsoleCursorInfo fails
        /// </exception>

        internal static CONSOLE_CURSOR_INFO GetConsoleCursorInfo(ConsoleHandle consoleHandle)
        {
            Util.Assert(!consoleHandle.IsInvalid, "ConsoleHandle is not valid");
            Util.Assert(!consoleHandle.IsClosed, "ConsoleHandle is closed");

            CONSOLE_CURSOR_INFO cursorInfo;

            bool result = NativeMethods.GetConsoleCursorInfo(consoleHandle.DangerousGetHandle(), out cursorInfo);

            if (result == false)
            {
                int err = Marshal.GetLastWin32Error();

                HostException e = CreateHostException(err, "GetConsoleCursorInfo",
                    ErrorCategory.ResourceUnavailable, "The Win32 internal error \"{0}\" 0x{1:X} occurred while getting cursor information. Contact Microsoft Customer Support Services." ); //ConsoleControlStrings.GetConsoleCursorInfoExceptionTemplate);
                throw e;
            }
            return cursorInfo;
        }

        internal static CONSOLE_FONT_INFO_EX GetConsoleFontInfo(ConsoleHandle consoleHandle)
        {
            Util.Assert(!consoleHandle.IsInvalid, "ConsoleHandle is not valid");
            Util.Assert(!consoleHandle.IsClosed, "ConsoleHandle is closed");

            CONSOLE_FONT_INFO_EX fontInfo = new CONSOLE_FONT_INFO_EX();
            fontInfo.cbSize = Marshal.SizeOf(fontInfo);
            bool result = NativeMethods.GetCurrentConsoleFontEx(consoleHandle.DangerousGetHandle(), false, ref fontInfo);

            if (result == false)
            {
                int err = Marshal.GetLastWin32Error();

                HostException e = CreateHostException(err, "GetConsoleFontInfo",
                    ErrorCategory.ResourceUnavailable, "The Win32 internal error \"{0}\" 0x{1:X} occurred while getting console font information. Contact Microsoft Customer Support Services." ); //ConsoleControlStrings.GetConsoleFontInfoExceptionTemplate);
                throw e;
            }
            return fontInfo;
        }

        /// <summary>
        /// Wraps Win32 SetConsoleCursorInfo
        /// </summary>
        /// <param name="consoleHandle">
        /// 
        /// handle for the console where cursor info is set
        /// 
        /// </param>
        /// <param name="cursorInfo">
        /// 
        /// cursor info to set the cursor
        /// 
        /// </param>
        /// <exception cref="HostException">
        /// If Win32's SetConsoleCursorInfo fails
        /// </exception>

        internal static void SetConsoleCursorInfo(ConsoleHandle consoleHandle, CONSOLE_CURSOR_INFO cursorInfo)
        {
            Util.Assert(!consoleHandle.IsInvalid, "ConsoleHandle is not valid");
            Util.Assert(!consoleHandle.IsClosed, "ConsoleHandle is closed");

            bool result = NativeMethods.SetConsoleCursorInfo(consoleHandle.DangerousGetHandle(), ref cursorInfo);

            if (result == false)
            {
                int err = Marshal.GetLastWin32Error();

                HostException e = CreateHostException(err, "SetConsoleCursorInfo",
                    ErrorCategory.ResourceUnavailable, "Win32 internal error \"{0}\" 0x{1:X} occurred while setting cursor information.Contact Microsoft Customer Support Services." ); //ConsoleControlStrings.SetConsoleCursorInfoExceptionTemplate);
                throw e;
            }
        }

#endregion Cursor

#region helper

        /// <summary>
        /// Helper function to create the proper HostException
        /// </summary>
        /// <param name="win32Error"></param>
        /// <param name="errorId"></param>
        /// <param name="category"></param>
        /// <param name="resourceStr"></param>
        /// <returns></returns>
        private static HostException CreateHostException(
            int win32Error, string errorId, ErrorCategory category, string resourceStr)
        {
            Win32Exception innerException = new Win32Exception(win32Error);
            string msg = Util.Sprintf(resourceStr, innerException.Message, win32Error);
            HostException e = new HostException(msg, innerException, errorId, category);
            return e;
        }

#endregion helper

#region SendInput

        internal static void MimicKeyPress(INPUT[] inputs)
        {
            Util.Assert(inputs != null && inputs.Length > 0, "inputs should not be null or empty");
            var numberOfSuccessfulEvents = NativeMethods.SendInput((uint)inputs.Length, inputs, Marshal.SizeOf(typeof(INPUT)));

            if (numberOfSuccessfulEvents == 0)
            {
                int err = Marshal.GetLastWin32Error();

                HostException e = CreateHostException(err, "SendKeyPressInput",
                    ErrorCategory.ResourceUnavailable, "The Win32 internal error \"{0}\" 0x{1:X} occurred while sending keyboard input. Contact Microsoft Customer Support Services." ); //ConsoleControlStrings.SendKeyPressInputExceptionTemplate);
                throw e;
            }
        }

#endregion SendInput

        /// <summary>
        /// 
        /// Class to hold the Native Methods used in this file enclosing class.
        /// 
        /// </summary>

        internal static class NativeMethods
        {
            internal static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);  // WinBase.h
            internal const int FontTypeMask = 0x06;
            internal const int TrueTypeFont = 0x04;

#region CreateFile

            [Flags]
            internal enum AccessQualifiers : uint
            {
                // From winnt.h
                GenericRead = 0x80000000,
                GenericWrite = 0x40000000
            }

            [Flags]
            internal enum ShareModes : uint
            {
                // From winnt.h
                ShareRead = 0x00000001,
                ShareWrite = 0x00000002
            }

            internal enum CreationDisposition : uint
            {
                // From winbase.h
                CreateNew = 1,
                CreateAlways = 2,
                OpenExisting = 3,
                OpenAlways = 4,
                TruncateExisting = 5
            }

            [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            internal static extern NakedWin32Handle CreateFile
            (
                string fileName,
                DWORD desiredAccess,
                DWORD ShareModes,
                IntPtr securityAttributes,
                DWORD creationDisposition,
                DWORD flagsAndAttributes,
                NakedWin32Handle templateFileWin32Handle
            );

#endregion CreateFile

#region Code Page

            [DllImport("kernel32.dll", SetLastError = false, CharSet = CharSet.Unicode)]
            internal static extern uint GetConsoleCP();

            [DllImport("kernel32.dll", SetLastError = false, CharSet = CharSet.Unicode)]
            internal static extern uint GetConsoleOutputCP();

#endregion Code Page

            [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            internal static extern HWND GetConsoleWindow();

            [DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            internal static extern HDC GetDC(HWND hwnd);

            [DllImport("User32.dll", SetLastError = false, CharSet = CharSet.Unicode)]
            internal static extern int ReleaseDC(HWND hwnd, HDC hdc);

            [DllImport("GDI32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            internal static extern bool TranslateCharsetInfo(IntPtr src, out CHARSETINFO Cs, DWORD options);

            [DllImport("GDI32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            internal static extern bool GetTextMetrics(HDC hdc, out TEXTMETRIC tm);

            [DllImport("GDI32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            internal static extern bool GetCharWidth32(HDC hdc, uint first, uint last, out int width);

            [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            internal static extern bool FlushConsoleInputBuffer(NakedWin32Handle consoleInput);

            [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            internal static extern bool FillConsoleOutputAttribute
            (
                NakedWin32Handle consoleOutput,
                WORD attribute,
                DWORD length,
                COORD writeCoord,
                out DWORD numberOfAttrsWritten
            );

            [DllImport("KERNEL32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            internal static extern bool FillConsoleOutputCharacter
            (
                NakedWin32Handle consoleOutput,
                Char character,
                DWORD length,
                COORD writeCoord,
                out DWORD numberOfCharsWritten
            );

            // [zhent] unlike PowerShell Core, we use a ref char instead of char*. This doesn't require compiling with /unsafe,
            // allowing us pretend we are doing something more safe (we aren't).
            [DllImport( "KERNEL32.dll", EntryPoint = "WriteConsole", SetLastError = true, CharSet = CharSet.Unicode )]
            internal static extern bool WriteConsole
            (
                SafeHandle consoleOutput,
                in char buffer,
                DWORD numberOfCharsToWrite,
                out DWORD numberOfCharsWritten,
                IntPtr reserved
            );

            [DllImport("KERNEL32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            internal static extern DWORD GetConsoleTitle(StringBuilder consoleTitle, DWORD size);

            [DllImport("KERNEL32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            internal static extern bool SetConsoleTitle(string consoleTitle);

            [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            internal static extern bool GetConsoleMode(NakedWin32Handle consoleHandle, out UInt32 mode);

            [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            internal static extern bool GetConsoleScreenBufferInfo(NakedWin32Handle consoleHandle, out CONSOLE_SCREEN_BUFFER_INFO consoleScreenBufferInfo);


            internal enum FileType
            {
                Unknown,
                Disk,
                Char,
                Pipe
            };

            [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            internal static extern FileType GetFileType(NakedWin32Handle fileHandle);

            [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            internal static extern COORD GetLargestConsoleWindowSize(NakedWin32Handle consoleOutput);

            [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            internal static extern NakedWin32Handle GetStdHandle(DWORD handleId);

            [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            internal static extern bool ReadConsole
            (
                NakedWin32Handle consoleInput,
                StringBuilder buffer,
                DWORD numberOfCharsToRead,
                out DWORD numberOfCharsRead,
                // This magical parameter is not documented, but is the secret to tab-completion.
                ref CONSOLE_READCONSOLE_CONTROL controlData
            );

            [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            internal static extern bool PeekConsoleInput
            (
                NakedWin32Handle consoleInput,
                [Out] INPUT_RECORD[] buffer,
                DWORD length,
                out DWORD numberOfEventsRead
            );

            [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            internal static extern bool GetNumberOfConsoleInputEvents(NakedWin32Handle consoleInput, out DWORD numberOfEvents);

            [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            internal static extern bool SetConsoleCtrlHandler(BreakHandler handlerRoutine, bool add);

            [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            internal static extern bool SetConsoleCursorPosition(NakedWin32Handle consoleOutput, COORD cursorPosition);

            [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            internal static extern bool SetConsoleMode(NakedWin32Handle consoleHandle, DWORD mode);

            [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            internal static extern bool SetConsoleScreenBufferSize(NakedWin32Handle consoleOutput, COORD size);

            [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            internal static extern bool SetConsoleTextAttribute(NakedWin32Handle consoleOutput, WORD attributes);

            [DllImport("KERNEL32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            internal static extern bool SetConsoleWindowInfo(NakedWin32Handle consoleHandle, bool absolute, ref SMALL_RECT windowInfo);

            [DllImport("KERNEL32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            internal static extern bool WriteConsoleOutput
            (
                NakedWin32Handle consoleOutput,
                CHAR_INFO[] buffer,
                COORD bufferSize,
                COORD bufferCoord,
                ref SMALL_RECT writeRegion
            );

            [DllImport("KERNEL32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            internal static extern bool ReadConsoleOutput
            (
                NakedWin32Handle consoleOutput,
                [Out] CHAR_INFO[] buffer,
                COORD bufferSize,
                COORD bufferCoord,
                ref SMALL_RECT readRegion
            );

            [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            internal static extern bool ScrollConsoleScreenBuffer
            (
                NakedWin32Handle consoleOutput,
                ref SMALL_RECT scrollRectangle,
                ref SMALL_RECT clipRectangle,
                COORD destinationOrigin,
                ref CHAR_INFO fill
            );

            [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            internal static extern UInt32 SendInput(UInt32 inputNumbers, INPUT[] inputs, Int32 sizeOfInput);

            // There is no GetCurrentConsoleFontEx on Core
            [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            internal static extern bool GetCurrentConsoleFontEx(NakedWin32Handle consoleOutput, bool bMaximumWindow, ref CONSOLE_FONT_INFO_EX consoleFontInfo);

            // There is no SetCurrentConsoleFontEx on Core
            [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            internal static extern bool SetCurrentConsoleFontEx(NakedWin32Handle consoleOutput, bool bMaximumWindow, ref CONSOLE_FONT_INFO_EX consoleFontInfo);

            [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            internal static extern bool GetConsoleCursorInfo(NakedWin32Handle consoleOutput, out CONSOLE_CURSOR_INFO consoleCursorInfo);

            [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            internal static extern bool SetConsoleCursorInfo(NakedWin32Handle consoleOutput, ref CONSOLE_CURSOR_INFO consoleCursorInfo);

            [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            internal static extern bool ReadConsoleInput
            (
                NakedWin32Handle consoleInput,
                [Out] INPUT_RECORD[] buffer,
                DWORD length,
                out DWORD numberOfEventsRead
            );

            internal enum CHAR_INFO_Attributes : uint
            {
                COMMON_LVB_LEADING_BYTE = 0x0100,
                COMMON_LVB_TRAILING_BYTE = 0x0200
            }
        }

        private const string StringsResourceBaseName = "ConsoleControlStrings";
        /*private const string AddBreakHandlerTemplateResource = ConsoleControlStrings.AddBreakHandlerExceptionTemplate;
        private const string RemoveBreakHandlerTemplateResource = ConsoleControlStrings.RemoveBreakHandlerExceptionTemplate;
        private const string AttachToParentConsoleTemplateResource = ConsoleControlStrings.AttachToParentConsoleExceptionTemplate;
        private const string DetachFromConsoleTemplateResource = ConsoleControlStrings.DetachFromConsoleExceptionTemplate;
        private const string GetInputHandleTemplateResource = ConsoleControlStrings.GetInputModeExceptionTemplate;
        private const string GetActiveScreenBufferHandleTemplateResource = ConsoleControlStrings.GetActiveScreenBufferHandleExceptionTemplate;
        private const string GetModeTemplateResource = ConsoleControlStrings.GetModeExceptionTemplate;
        private const string SetModeTemplateResource = ConsoleControlStrings.SetModeExceptionTemplate;
        private const string ReadConsoleTemplateResource = ConsoleControlStrings.ReadConsoleExceptionTemplate;
        private const string ReadConsoleInputTemplateResource = ConsoleControlStrings.ReadConsoleInputExceptionTemplate;
        private const string PeekConsoleInputTemplateResource = ConsoleControlStrings.PeekConsoleInputExceptionTemplate;
        private const string GetNumberOfConsoleInputEventsTemplateResource = ConsoleControlStrings.GetNumberOfConsoleInputEventsExceptionTemplate;
        private const string FlushConsoleInputBufferTemplateResource = ConsoleControlStrings.FlushConsoleInputBufferExceptionTemplate;
        private const string GetConsoleScreenBufferInfoTemplateResource = ConsoleControlStrings.GetConsoleScreenBufferInfoExceptionTemplate;
        private const string SetConsoleScreenBufferSizeTemplateResource = ConsoleControlStrings.SetConsoleScreenBufferSizeExceptionTemplate;
        private const string WriteConsoleOutputTemplateResource = ConsoleControlStrings.WriteConsoleOutputExceptionTemplate;
        private const string ReadConsoleOutputTemplateResource = ConsoleControlStrings.ReadConsoleOutputExceptionTemplate;
        private const string FillConsoleOutputCharacterTemplateResource = ConsoleControlStrings.FillConsoleOutputCharacterExceptionTemplate;
        private const string FillConsoleOutputAttributeTemplateResource = ConsoleControlStrings.FillConsoleOutputAttributeExceptionTemplate;
        private const string ScrollConsoleScreenBufferTemplateResource = ConsoleControlStrings.ScrollConsoleScreenBufferExceptionTemplate;
        private const string SetConsoleWindowInfoTemplateResource = ConsoleControlStrings.SetConsoleWindowInfoExceptionTemplate;
        private const string GetLargestConsoleWindowSizeTemplateResource = ConsoleControlStrings.GetLargestConsoleWindowSizeExceptionTemplate;
        private const string SetConsoleWindowTitleTemplateResource = ConsoleControlStrings.SetConsoleWindowTitleExceptionTemplate;
        private const string WriteConsoleTemplateResource = ConsoleControlStrings.WriteConsoleExceptionTemplate;
        private const string SetConsoleTextAttributeTemplateResource = ConsoleControlStrings.SetConsoleTextAttributeExceptionTemplate;
        private const string SetConsoleCursorPositionTemplateResource = ConsoleControlStrings.SetConsoleCursorPositionExceptionTemplate;
        private const string GetConsoleCursorInfoTemplateResource = ConsoleControlStrings.GetConsoleCursorInfoExceptionTemplate;
        private const string SetConsoleCursorInfoTemplateResource = ConsoleControlStrings.SetConsoleCursorInfoExceptionTemplate;*/

        //[TraceSourceAttribute("ConsoleControl", "Console control methods")]
        //private static PSTraceSource tracer = PSTraceSource.GetTracer("MS.DbgShell.ConsoleControl", "DbgShell Console control methods");


        private static ConsoleHandle _GetBufferInfo( out CONSOLE_SCREEN_BUFFER_INFO bufferInfo )
        {
            ConsoleHandle result = ConsoleControl.GetActiveScreenBufferHandle();
            bufferInfo = ConsoleControl.GetConsoleScreenBufferInfo( result );
            return result;
        } // end _GetBufferInfo()


        private const string InvalidConsoleColorErrorResource = "Cannot set the console color because the value specified is not valid. Specify a valid color as defined by the System.ConsoleColor type."; //"InvalidConsoleColorError";

        public static ConsoleColor ForegroundColor
        {
            get
            {
                CONSOLE_SCREEN_BUFFER_INFO bufferInfo;
                _GetBufferInfo( out bufferInfo );

                ConsoleColor foreground;
                ConsoleColor unused;

                WORDToColor( bufferInfo.Attributes, out foreground, out unused );
                return foreground;
            }
            set
            {
                if( IsConsoleColor( value ) )
                {
                    CONSOLE_SCREEN_BUFFER_INFO bufferInfo;

                    ConsoleHandle handle = _GetBufferInfo( out bufferInfo );

                    // mask in the foreground from the current color.
                    short a = (short) bufferInfo.Attributes;

                    a &= (short) ~0x0f;
                    a = (short) ((ushort) a | (ushort) value);
                    SetConsoleTextAttribute( handle, (WORD) a );
                }
                else
                {
                    throw new ArgumentException( InvalidConsoleColorErrorResource, "value" );
                }
            }
        } // end property ForegroundColor



        public static ConsoleColor BackgroundColor
        {
            get
            {
                CONSOLE_SCREEN_BUFFER_INFO bufferInfo;
                _GetBufferInfo( out bufferInfo );

                ConsoleColor background;
                ConsoleColor unused;

                WORDToColor( bufferInfo.Attributes, out unused, out background );
                return background;
            }
            set
            {
                if( ConsoleControl.IsConsoleColor( value ) )
                {
                    CONSOLE_SCREEN_BUFFER_INFO bufferInfo;

                    ConsoleHandle handle = _GetBufferInfo( out bufferInfo );

                    // mask in the background from the current color.
                    short a = (short) bufferInfo.Attributes;

                    a &= (short) ~0xf0;
                    a = (short) ((ushort) a | (ushort) ((uint) value << 4));
                    SetConsoleTextAttribute( handle, (WORD) a );
                }
                else
                {
                    throw new ArgumentException( InvalidConsoleColorErrorResource, "value" );
                }
            }
        } // end property BackgroundColor
    }
}   // namespace 

